C++编程实战:从快递运费计算到质数分解的算法思维训练

1. 项目概述:从“Hello World”到解决实际问题

上次我们聊了C++的编译运行和基础语法,算是跟这门语言打了个招呼。但光会写“Hello World”可不行,那感觉就像刚拿到驾照,只敢在空旷的停车场里转圈。真正的编程乐趣,在于用代码去解决一个个具体的问题。今天,我们就来点实际的,我会带你亲手写几个能解决真实需求的小程序。比如,怎么根据快递件数和是否加急来计算运费?怎么判断一个数是不是两个质数的乘积,并找出那个大的质数?这些题目听起来是不是比单纯的语法练习有意思多了?

通过解决这些问题,你不仅能巩固变量、条件判断、循环这些基础知识,更重要的是,你会开始建立“计算思维”——如何把一个现实世界的规则,翻译成计算机能理解和执行的精确步骤。这个过程,才是编程入门的精髓。无论你未来是想做游戏开发、算法研究,还是任何与软件相关的领域,这种能力都是基石。我当年就是从这些“小题目”开始,一步步体会到,原来我写的几行代码真的能让电脑“听话”地去工作,那种成就感是无可替代的。好了,闲话少叙,我们直接进入正题,看看怎么用C++来搞定这些任务。

2. 核心需求解析:理解问题,设计算法

在动手写代码之前,我们得先当一回“翻译官”和“建筑师”。翻译官负责把模糊的人类语言(需求)变成清晰的逻辑步骤;建筑师则负责设计这些步骤在程序里的结构。我们以两个热搜题目为例,来拆解这个过程。

2.1 快递运费计算器:业务逻辑的代码化

题目要求:根据快递的件数和用户是否选择加急来计算快递费。计算规则:件数在10件以内(含10件),每件运费5元;超过10件,超过的部分每件3元。如果用户选择加急,总运费额外加收20元。

第一步:抽象与建模 这本质上是一个根据输入条件,应用不同计算规则,得到最终结果的数学函数。我们需要明确:

  1. 输入(Input) :两个变量。一个是整数类型的 件数 ,另一个是布尔类型或字符类型的 是否加急
  2. 处理逻辑(Process)
    • 判断件数是否大于10。
    • 如果否,总运费 = 件数 * 5。
    • 如果是,总运费 = 10 * 5 + (件数 - 10) * 3。
    • 判断是否加急。
    • 如果是,总运费 = 总运费 + 20。
  3. 输出(Output) :一个浮点数或整数类型的 总运费

第二步:边界与异常考虑

  • 件数应该是正整数。如果用户输入了0或负数怎么办?虽然题目没说,但一个健壮的程序应该处理这种无效输入,比如给出错误提示。
  • “是否加急”通常用‘y’/‘n’或‘1’/‘0’来表示,我们需要在代码里明确约定并处理用户的可能输入(大小写问题)。

这个例子完美地串联了 变量定义、算术运算、条件判断(if-else) 这几个核心知识点。

2.2 质数乘积分解:算法思维的初探

题目要求:已知正整数 n 是两个不同的质数的乘积,试求出两者中较大的那个质数。

第一步:问题转化 这不再是一个简单的公式计算,而是一个需要“寻找”和“验证”的过程。已知结果n,求构成它的两个因数(且必须是质数)。一个最直观(但可能低效)的思路是:

  1. 从最小的质数开始尝试,令其为 i (比如从2开始)。
  2. 检查 i 是否能整除 n (即 n % i == 0 )。如果能,则得到另一个因数 j = n / i
  3. 验证 i j 是否都是质数。
  4. 如果都是,那么较大的那个就是答案。
  5. 如果不是,则 i 递增,继续尝试。

第二步:核心难点与优化 这里引入了几个新概念:

  • 循环(Loop) :我们需要让 i 从2开始,一直尝试到 n-1 (实际上到 sqrt(n) 就够了,这是重要的优化点)。
  • 函数(Function) :判断一个数是否为质数,是一个独立且可能被多次使用的功能。将其写成一个独立的函数(比如 bool isPrime(int num) )会让代码更清晰、更易维护。
  • 算法效率 :从2遍历到n是一种“暴力枚举”。当n很大时(比如上亿),程序会非常慢。我们会在实现部分讨论如何优化质数判断和循环范围。

这个题目是迈向“算法”世界的第一步,它锻炼的是 循环控制、函数封装和基础数论知识 的应用。

注意 :在开始编码前,花几分钟在纸上画画流程图,或者写写伪代码,能极大减少你调试的时间。别急着直接敲键盘。

3. 开发环境与工具链的深度配置

工欲善其事,必先利其器。一个顺手的开发环境能让你专注于逻辑,而不是和工具搏斗。上期我们简单介绍了用文本编辑器+命令行编译的方式。这次,我们向“集成开发环境(IDE)”迈进一小步,让写代码、编译、调试更一体化。虽然很多热搜指向VSCode配置,但对于纯C++入门,我反而想先推荐更“纯粹”轻量的方案。

3.1 编辑器进阶:VS Code的智能助力

VS Code不是一个完整的IDE,但它通过插件可以变得非常强大。对于C++学习,安装以下插件至关重要:

  1. C/C++ (Microsoft) :提供代码智能感知(自动补全)、语法高亮、错误提示、跳转到定义、查看引用等核心功能。
  2. Code Runner :允许你一键运行多种语言的代码片段,非常方便快速测试。

配置编译与运行任务(tasks.json) 这是关键一步,让F5键能直接编译运行你的程序。在项目文件夹下创建 .vscode 文件夹,里面放一个 tasks.json 文件。

{
    "version": "2.0.0",
    "tasks": [
        {
            "label": "build and run c++",
            "type": "shell",
            "command": "g++", // 如果你用GCC
            // "command": "clang++", // 如果你用Clang
            "args": [
                "-fdiagnostics-color=always",
                "-g",
                "${file}",
                "-o",
                "${fileDirname}/${fileBasenameNoExtension}.exe", // Windows
                // "-o", "${fileDirname}/${fileBasenameNoExtension}", // Linux/macOS
                "&&",
                "${fileDirname}/${fileBasenameNoExtension}.exe"
            ],
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "presentation": {
                "echo": true,
                "reveal": "always",
                "focus": false,
                "panel": "shared"
            },
            "problemMatcher": ["$gcc"]
        }
    ]
}

这个配置做了件事:用g++编译当前文件,生成同名exe,并立即运行它。你可以在VS Code的终端里看到输出。

调试配置(launch.json) 更强大的是调试。创建 launch.json

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "(gdb) Launch",
            "type": "cppdbg",
            "request": "launch",
            "program": "${fileDirname}/${fileBasenameNoExtension}.exe",
            "args": [],
            "stopAtEntry": false,
            "cwd": "${fileDirname}",
            "environment": [],
            "externalConsole": false, // 设为true可使用独立控制台,输入体验更好
            "MIMode": "gdb",
            "miDebuggerPath": "gdb",
            "setupCommands": [
                {
                    "description": "Enable pretty-printing for gdb",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true
                }
            ],
            "preLaunchTask": "build and run c++" // 关联上面的编译任务
        }
    ]
}

配置好后,你可以在代码行号左边点击设置断点,然后按F5启动调试。程序会停在断点处,你可以查看所有变量的当前值,单步执行,这是理解程序运行流程、查找Bug的终极利器。

3.2 编译器的选择与常见问题

热搜里频繁出现“Microsoft Visual C++ Redistributable”和编译错误,这里集中解释一下。

  • 编译器(Compiler) :如GCC(MinGW)、Clang、MSVC。它们负责将你的C++源代码翻译成机器码。我们之前用的 g++ 就是GCC的C++编译器。
  • 运行时库(Redistributable) :像“Visual C++ 2005/2008/2010... Redistributable”,这是微软VC编译器编译出的程序在 运行 时所依赖的一组动态链接库(DLL)。如果你的程序用MSVC编译,想在别人的电脑上运行,可能需要安装对应版本的Redistributable。
  • 开发库(SDK) :编写和编译程序时需要的一组头文件和库文件。

常见问题“安装node.js时显示microsoft visual c++ 2022 x86 minimum runtime安装包不存在”解析 : 这不是一个C++开发问题,而是其他软件(如Node.js, Python包)的安装器试图为你安装VC运行时库,但失败了。解决方法通常是:

  1. 直接去微软官网下载并独立安装最新版的“Microsoft Visual C++ Redistributable for Visual Studio 2022”。
  2. 或者,如果你安装了完整版的Visual Studio IDE,运行时库通常已经包含。

对于纯C++学习,我建议在Windows上使用 MinGW-w64 来提供GCC编译器,它生成的程序通常不依赖额外的VC运行时库,更干净。你可以通过MSYS2或直接下载MinGW-w64安装包来获取。

3.3 项目管理:从单个文件到多个文件

当你代码越来越多,把不同功能的代码分开到不同文件是必然的。最基本的是将声明和实现分离。

  • 头文件(.h 或 .hpp) :存放函数声明、类定义、常量定义等。例如,我们可以创建一个 prime_utils.h
    // prime_utils.h
    #ifndef PRIME_UTILS_H // 防止头文件被重复包含
    #define PRIME_UTILS_H
    
    bool isPrime(int num); // 函数声明
    
    #endif
    
  • 源文件(.cpp) :存放函数的具体实现。例如, prime_utils.cpp
    // prime_utils.cpp
    #include “prime_utils.h”
    #include <cmath>
    
    bool isPrime(int num) {
        if (num <= 1) return false;
        for (int i = 2; i <= sqrt(num); ++i) { // 优化:只需检查到平方根
            if (num % i == 0) return false;
        }
        return true;
    }
    
  • 主程序文件(main.cpp) :包含 main 函数,并 #include 所需的头文件。
    // main.cpp
    #include <iostream>
    #include “prime_utils.h”
    
    int main() {
        int n = 15;
        if (isPrime(n)) {
            std::cout << n << “ is prime.” << std::endl;
        }
        return 0;
    }
    

编译多文件程序

g++ -c prime_utils.cpp -o prime_utils.o # 编译,生成目标文件
g++ -c main.cpp -o main.o
g++ main.o prime_utils.o -o my_program.exe # 链接,生成可执行文件
# 或者一步到位
g++ main.cpp prime_utils.cpp -o my_program.exe

在VS Code中,你需要确保 tasks.json 里的 args 包含了所有需要编译的 .cpp 文件,或者使用更高级的构建系统如CMake,但这对于入门初期稍显复杂,可以先按单文件或简单多文件方式处理。

4. 核心语法深化与项目实战

有了环境和理论基础,我们现在动手实现那两个项目。我会在代码中穿插讲解涉及到的语法细节和设计考量。

4.1 项目一:快递运费计算器实现

这是一个典型的控制台交互程序,涉及输入、处理、输出。

#include <iostream>
#include <cctype> // 用于字符处理

int main() {
    int packageCount;
    char isUrgent;
    double totalCost = 0.0;

    // 1. 获取用户输入
    std::cout << “请输入快递件数(正整数): “;
    std::cin >> packageCount;

    // 输入验证(基础版)
    if (packageCount <= 0) {
        std::cout << “错误:件数必须为正整数。” << std::endl;
        return 1; // 非正常退出
    }

    std::cout << “是否加急?(y/Y 表示加急,其他表示不加急): “;
    std::cin >> isUrgent;

    // 2. 计算基础运费
    if (packageCount <= 10) {
        totalCost = packageCount * 5.0;
    } else {
        totalCost = 10 * 5.0 + (packageCount - 10) * 3.0;
    }

    // 3. 判断是否加急
    // 将字符转换为小写再判断,更友好
    if (tolower(isUrgent) == ‘y’) {
        totalCost += 20.0;
        std::cout << “【已选择加急服务】” << std::endl;
    }

    // 4. 输出结果
    std::cout << “快递件数: “ << packageCount << std::endl;
    std::cout << “总计运费: “ << totalCost << “ 元” << std::endl;

    return 0;
}

代码解读与技巧

  • 数据类型选择 totalCost 用了 double ,虽然运费通常是整数,但考虑到未来可能有折扣(如9.5折),使用浮点数更稳妥。 packageCount int ,因为件数肯定是整数。
  • 输入验证 :这是一个好习惯。我们检查了件数是否为正。更完善的验证还包括检查 std::cin 是否处于失败状态(比如用户输入了字母)。
  • 字符处理 :使用 tolower() 函数将用户输入的字符统一转为小写,这样无论输入‘y’还是‘Y’都能正确识别,提升了用户体验。
  • 清晰的输出 :将计算规则和结果清晰地打印出来,方便用户核对。

扩展思考 : 如果运费规则更复杂,比如不同地区基础运费不同,或者有会员折扣。你会怎么设计代码?可能会引入更多的变量、更复杂的条件判断,甚至可以考虑使用 switch 语句或查找表(数组)来管理地区费率。

4.2 项目二:质数乘积分解器实现

这个项目需要我们实现一个质数判断函数,并在主逻辑中循环查找。

#include <iostream>
#include <cmath> // 使用 sqrt 函数

// 函数声明 (如果函数定义在main之后,则需要先声明)
bool isPrime(int num);

int main() {
    int n;
    std::cout << “请输入一个正整数 n (是两个不同质数的乘积): “;
    std::cin >> n;

    if (n <= 3) {
        std::cout << “n 太小,不符合两个不同质数乘积的条件。” << std::endl;
        return 1;
    }

    int largerPrime = 0;
    bool found = false;

    // 核心查找逻辑
    // 优化1:只需检查到 sqrt(n),因为如果有一个大于 sqrt(n) 的因子,必然对应一个小于 sqrt(n) 的因子。
    int limit = static_cast<int>(std::sqrt(n));
    for (int i = 2; i <= limit && !found; ++i) { // 优化2:找到即停止循环
        if (n % i == 0) {
            // i 是 n 的一个因子
            int j = n / i;
            // 检查 i 和 j 是否都是质数,并且不相同
            if (isPrime(i) && isPrime(j) && i != j) {
                found = true;
                largerPrime = (i > j) ? i : j; // 三元运算符,取较大者
            }
        }
    }

    if (found) {
        std::cout << “较大的那个质数是: “ << largerPrime << std::endl;
    } else {
        std::cout << “未找到符合条件的两个不同质数。请确认输入是否正确。” << std::endl;
    }

    return 0;
}

// 函数定义:判断一个整数是否为质数
bool isPrime(int num) {
    if (num <= 1) return false; // 1及以下的数不是质数
    if (num == 2) return true;  // 2是唯一的偶质数
    if (num % 2 == 0) return false; // 排除其他偶数

    // 优化:只需检查奇数因子,直到 sqrt(num)
    int limit = static_cast<int>(std::sqrt(num));
    for (int i = 3; i <= limit; i += 2) { // i从3开始,每次加2
        if (num % i == 0) {
            return false;
        }
    }
    return true;
}

代码解读与深度优化

  • 函数的使用 :将 isPrime 独立成函数,使得主逻辑 main 非常清晰。这是代码可读性和可复用性的关键。
  • 算法优化点
    1. 循环边界 :在 main 中,因子只需检查到 sqrt(n) 。这是一个巨大的性能提升。例如n=10000,只需检查到100,而不是9999。
    2. 提前终止 :一旦找到符合条件的质数对,立即通过 found 标志终止循环。
    3. 质数判断优化 :在 isPrime 函数中,首先排除了偶数(除了2),并且只用在奇数里寻找因子,循环步长设为2。这又将检查次数减少了一半。
  • 类型转换 static_cast<int>(std::sqrt(num)) sqrt 返回的 double 显式转换为 int ,用于循环比较。这是一种安全的类型转换方式。
  • 错误处理 :对输入进行了基本的合理性判断,并提供了未找到结果时的反馈。

实操心得 :在写循环和条件判断时,时刻思考“这个循环真的需要跑这么多次吗?”和“这个条件能提前结束吗?”。养成优化思维,即使对小程序也大有裨益。另外,像 isPrime 这样的通用函数,完全可以保存到你的个人代码工具库中,以后直接复用。

5. 调试技巧与常见问题实录

代码写完了,跑起来不对怎么办?别慌,调试是每个程序员的必修课。除了使用VS Code的图形化调试器,掌握一些基本的“土法”调试和常见错误排查也至关重要。

5.1 调试“三板斧”

  1. 打印大法(cout调试) :在怀疑出问题的代码位置前后,打印关键变量的值。这是最直接、最古老也最有效的方法之一。

    // 在质数查找循环里
    for (int i = 2; i <= limit; ++i) {
        std::cout << “[DEBUG] Trying i=“ << i << std::endl; // 调试输出
        if (n % i == 0) {
            int j = n / i;
            std::cout << “[DEBUG] Found factor pair: “ << i << “, “ << j << std::endl;
            // ... 后续判断
        }
    }
    

    通过观察输出,你可以清楚地看到程序执行到了哪里,变量的值是否符合预期。

  2. 断言(assert) :在代码中插入一些必须为真的条件检查,如果为假,程序会立即终止并报错。适用于检查一些“绝对不应该发生”的情况。

    #include <cassert>
    int divide(int a, int b) {
        assert(b != 0 && “Divisor cannot be zero!”); // 如果b为0,程序崩溃并提示信息
        return a / b;
    }
    

    在发布版本中,可以通过定义 NDEBUG 宏来禁用所有 assert

  3. 使用调试器(Debugger) :如前所述,在VS Code中设断点、单步执行、观察变量窗口。这是查看程序运行时状态的终极工具。特别是对于循环和逻辑复杂的条件判断,单步跟踪能让你看清每一步的执行路径。

5.2 C++新手常见编译与运行时错误

下面这个表格整理了你很可能在初期遇到的“拦路虎”:

错误类型 典型报错信息/现象 原因分析 解决方案
编译错误 error: ‘cout’ was not declared in this scope 忘记包含必要的头文件 <iostream> ,或者误写为 <iostream.h> (旧式)。 在文件开头添加 #include <iostream>
error: expected initializer before ‘xxx’ 通常是因为上一行语句缺少分号 ; 检查报错位置的前一行代码,补上分号。
error: ‘xxx’ was not declared in this scope 变量或函数在使用前未声明。可能是拼写错误,或者函数定义在调用之后而未提前声明。 检查拼写;如果函数定义在后,需在调用前添加函数声明(如 int myFunc(); )。
链接错误 undefined reference to ‘isPrime(int)’ 编译器知道 isPrime 这个函数(因为声明了),但找不到它的实现体。在多文件项目中,可能是对应的 .cpp 文件没有被一起编译链接。 确保编译命令包含了所有实现文件( g++ main.cpp utils.cpp ),或者在IDE中正确配置了项目。
运行时错误 逻辑错误 :程序能运行,但结果不对。比如快递运费算少了。 算法逻辑有误。例如条件判断的边界( < 还是 <= )搞错,或者运算符优先级理解错误。 使用“打印大法”或调试器,在关键节点检查变量值,逐步缩小问题范围。仔细对照流程图或伪代码。
无限循环 :程序卡住,不结束。 循环条件永远为真。例如 while (i > 0) 但循环体内 i 从未减少。 检查循环变量的更新语句是否在正确的位置执行。在循环内打印循环变量值。
除零错误 :程序崩溃。 在执行除法或取模运算时,除数为0。 在进行除法运算前,务必检查除数是否为0。
输入流问题 cin 后程序跳过输入或行为异常。 输入缓冲区中残留了换行符或错误数据。例如,用 cin >> int 后紧接着用 getline 在混合使用 cin >> getline 时,在 cin >> 后使用 cin.ignore() 清空缓冲区。对输入进行有效性检查。

一个典型输入问题的例子

int age;
std::string name;
std::cout << “Enter your age: “;
std::cin >> age; // 用户输入”25\n”, age读取25, ‘\n’留在缓冲区
std::cout << “Enter your name: “;
std::getline(std::cin, name); // getline立刻读到缓冲区里的’\n’,以为输入结束,name为空

解决

std::cin >> age;
std::cin.ignore(); // 忽略掉缓冲区中残留的换行符
std::getline(std::cin, name);

5.3 思维误区:我写的排序哪错了?

热搜里有个很有意思的问题:“我c++写的排序哪错了”。这背后反映的是一种普遍的学习困境:代码能编译,但结果不对。抛开具体代码,排查这类问题的通用思路是:

  1. 隔离问题 :写一个最小测试用例。不要在你的大项目里调试,而是单独写一个只有排序函数和几个固定输入的小程序。
  2. 可视化过程 :对于排序算法,在每一轮循环后,把数组的当前状态打印出来。对比你的打印结果和手动模拟的结果,差异点就是bug所在。
  3. 边界检查 :排序算法最容易在边界条件上出错:数组第一个和最后一个元素处理了吗?循环的起止下标对吗?当数组为空或只有一个元素时,你的代码还能工作吗?
  4. 算法理解 :你真的理解你实现的排序算法(比如冒泡、选择、快速排序)的每一步在做什么吗?如果不确定,回头用纸笔模拟一遍算法的标准流程。

避坑技巧 :对于数组操作,务必警惕“下标越界”。C++不会自动检查数组访问是否越界,越界写入可能导致程序崩溃或出现难以预测的诡异行为。在循环中使用下标时,心里默念“数组长度是N,有效下标是0到N-1”。

6. 从入门到应用:下一步学什么?

完成了这两个小项目,你已经跨过了“只知道语法”的门槛,进入了“能用语法解决问题”的阶段。接下来,如果你想继续深入,方向有很多。

6.1 巩固基础:理解“内存”与“指针”

这是C++最独特也最令人困惑的部分,但也是其强大性能的根源。接下来你应该去理解:

  • 指针(Pointer) :什么是指针变量? & 取地址符和 * 解引用符分别做什么?指针为什么可以用于函数修改变量、动态内存分配和构建复杂数据结构(如链表、树)?
  • 引用(Reference) :和指针有什么区别?在函数传参时,何时用值传递、何时用指针传递、何时用引用传递?
  • 数组与指针的关系 :为什么数组名在很多情况下可以当作指针使用?
  • 动态内存管理 new delete 运算符。理解为什么需要它们(在编译时无法确定需要多少内存),以及忘记 delete 会导致的“内存泄漏”问题。

你可以尝试的项目:用指针实现一个简单的动态数组(类似 vector 的简化版),或者实现一个单向链表来管理一组数据。

6.2 面向对象初探:认识“类”与“对象”

C++被称为“带类的C”,面向对象是其核心范式。你需要理解:

  • 类(Class)和对象(Object) :如何把数据和操作数据的方法捆绑在一起?比如,我们可以定义一个 ExpressPackage 类,里面有 count , isUrgent 属性和 calculateCost() 方法。
  • 封装、继承、多态 :这三个核心概念是什么? public , private , protected 这些访问修饰符如何控制数据的隐藏与暴露?
  • 构造函数与析构函数 :对象如何被创建和初始化?又如何被清理?

尝试将快递运费计算器改写成面向对象的版本,定义一个 ExpressOrder 类,这会让你对抽象和封装有切身体会。

6.3 标准库入门:站在巨人的肩膀上

不要重复造轮子。C++标准库(STL)提供了大量已经写好的、高效且可靠的容器和算法。

  • 容器 vector (动态数组)、 string (字符串)、 map (键值对字典)是你最先应该掌握的。它们比原生数组和C风格字符串安全、方便得多。
  • 算法 <algorithm> 头文件里的 sort , find , count 等函数模板。用 std::sort 对你用数组实现的排序进行性能对比。
  • 输入输出流 :你已经用了 cin cout ,还可以了解下文件流 fstream ,用于读写文件。

学习使用 vector string 来重构你的程序,你会发现内存管理和字符串处理变得异常轻松。

6.4 实践驱动:寻找有趣的小项目

学编程最好的方式就是用它。热搜里提到的“C++小游戏”、“游戏编程”是绝佳的练手方向。从最简单的开始:

  1. 猜数字游戏 :电脑随机生成一个数,玩家来猜。
  2. 控制台版贪吃蛇 :利用 gotoxy (Windows)或 ncurses 库(Linux)控制光标位置,实现移动和增长。
  3. 简易计算器 :支持加减乘除,可以扩展支持括号、表达式解析。

这些项目会综合运用你学到的所有知识:变量、循环、条件、函数、数组/容器,甚至可能用到简单的状态机。遇到问题就去搜索(CSDN、Stack Overflow)、查阅资料(《C++ Primer》是经典),在解决问题的过程中,你的能力会飞速增长。

记住,编程是一门实践手艺。看懂和会写之间,隔着无数次的调试和重来。别怕出错,每一个你解决的bug,都会让你对计算机的理解更深一分。从今天这两个小程序出发,保持好奇,持续动手,你就能在C++的世界里越走越远。

「LLM那些事」系列第 4 篇《上下文窗口的边界》,文章连接:https://blog.csdn.net/houwenjin/article/details/163999753。 演示什么:在「预测」Sheet 的黄色格子里输入一句话(默认「来泡一杯」),四个「模型」——分别只统计最后 1 / 2 / 3 / 4 个字的 n-gram 查表——同时预测下一个字。同一个输入,看的上下文越长,候选越少、预测越确定: ┌────────────────┬──────────┬───────────────┬──────┐ │ 只看最后几个字 │ 用的前缀 │ 候选下一字数 │ 预测 │ ├────────────────┼──────────┼───────────────┼──────┤ │ 1 个 │ 杯 │ 3(茶/子/水) │ 模糊 │ ├────────────────┼──────────┼───────────────┼──────┤ │ 2 个 │ 一杯 │ 2(茶/水) │ 收窄 │ ├────────────────┼──────────┼───────────────┼──────┤ │ 3 个 │ 泡一杯 │ 1(茶) │ 确定 │ ├────────────────┼──────────┼───────────────┼──────┤ │ 4 个 │ 来泡一杯 │ 1(茶) │ 确定 │ └────────────────┴──────────┴───────────────┴──────┘
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值