《数学之美》——第21章 拼音输入法的数学原理
《数学之美》这本书不仅揭示了数学原理如何应用于搜索引擎的核心技术,还探讨了拼音输入法背后的数学与信息理论基础。第21章特别讲述了拼音输入法的工作原理,并强调了数学工具如信息熵、动态规划以及个性化语言模型的重要性。
输入法的实现需要解决两个主要问题:如何将拼音编码为计算机能够理解的形式,以及如何从多个可能的汉字中选择正确的汉字。这背后涉及到的是信息的编码与解码问题。拼音输入法通过使用较长的编码(全拼),使得输入过程更加自然且易于学习。同时,较长的编码也带来了更好的容错性,因为更多的字符意味着更多的信息冗余。
本章节中提到香农的第一定理,该定理表明任何有效的编码长度至少应等于信息熵。这意味着在设计输入法时,我们需要考虑到效率与准确性之间的平衡。为了提高输入法的性能,可以利用语言模型来预测下一个可能出现的汉字或词语,从而减少输入的不确定性。这实际上是在利用统计学的方法来优化输入体验,提高用户的输入速度和准确性。
此外,拼音转汉字的过程可以被视为一种优化问题,类似于在导航系统中寻找最短路径。这里所用的技术是动态规划,它是一种用于求解最优化问题的方法,非常适合处理这类需要从大量可能性中找出最佳解的任务。
个性化语言模型的概念体现了输入法技术的发展方向。通过分析用户的输入习惯和偏好,输入法可以变得更加智能,提供更为精准的预测结果。这不仅提升了用户体验,同时也展示了机器学习和个人化推荐系统的力量。
《数学之美》这本书不仅为我们揭示了技术背后的数学逻辑,更让我们看到了数学作为一种强大的工具,如何在日常生活中发挥着至关重要的作用。无论是搜索引擎还是拼音输入法,数学原理都在默默地支持着我们与数字世界的交互方式。通过阅读这样的书籍,我们可以更好地理解数学的应用价值,并激发我们探索更多数学在其他领域中的可能性。
本学期要学习的C++编程规范
免责声明:
本文仅供个人学习使用,内容来源于网络,如有侵权或不当使用,请联系我及时删除或修改。
内容总结自
https://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/index.html
Google开源项目风格指南——C++风格指南
一、头文件
通常每个.cc文件有一个配套的.h文件
1.自给自足
头文件自给自足,也就是能独立编译
2.#define 防护符
所有头文件都应该用 #define 防护符来防止重复导入. 防护符的格式是: <项目>_<路径>_<文件名>_H_
3.#incldue 的路径及顺序
推荐按照如下顺序导入头文件:
- 配套的头文件
- C语言系统的头文件
- C++标准库的头文件
- 其他库的头文件
- 本项目的头文件
头文件的路径应相对于项目源码目录
不能出现别名/.(当前目录)/…(上级目录)
二、作用域
1.命名空间
除了少数特殊情况, 应该在命名空间 (namespace) 内放置代码.
命名空间应该有独一无二的名字, 其中包含项目名称, 也可以选择性地包含文件路径.
禁止使用 using 指令 (例如 using namespace foo).
禁止使用内联 (inline) 命名空间.
2.局部变量
应该尽可能缩小函数变量的作用域 (scope), 并在声明的同时初始化.
如:
int i;
i = 1; //不建议
int i = 1; //建议
三、类
1.可拷贝类型和可移动类型
类的公有接口必须明确指明该类是可拷贝的、仅可移动的、还是既不可拷贝也不可移动的.
如果该类型的复制和移动操作有明确的语义并且有用,则应该支持这些操作.
2.结构体还是类
只能用 struct 定义那些用于储存数据的被动对象.
其他情况应该使用 class.
3.继承
通常情况下, 组合 (composition) 比继承 (inheritance) 更合适.
请使用 public 继承.
4.访问控制
类的 所有数据成员 应该声明为 私有 (private), 除非是常量.
这样做可以简化类的不变式 (invariant) 逻辑, 代价是需要增加一些冗余的访问器 (accessor) 代码 (通常是 const 方法).
5.声明次序
将相似的声明放在一起.
公有(public)部分放在最前面
类的定义通常以 public: 开头, 其次是 protected:, 最后以 private: 结尾. 空的部分可以省略.
四、函数
1.输入和输出
我们倾向于按值返回, 否则按引用返回。 避免返回指针, 除非它可以为空.
2.编写简短函数
我们倾向于编写简短, 凝练的函数.
如果函数超过 40 行, 可以思索一下能不能在不影响程序结构的前提下对其进行分割.
3.函数返回类型后置语法
只有在常规写法(返回值类型前置)不便于书写或不便于阅读时使用返回类型后置语法
C++ 现在允许两种不同的函数声明方式. 以往的写法是将返回类型置于函数名之前. 例如:
int foo(int x);
C++11 引入了这一新的形式. 现在可以在函数名前使用 auto 关键字, 在参数列表之后后置返回类型. 例如:
auto foo(int x) -> int;
五、来自 Google 的奇技
1.Cpplint
使用 cpplint.py 检查风格错误.
cpplint.py 是一个用来分析源文件, 能检查出多种风格错误的工具. 它不并完美, 甚至还会漏报和误报, 但它仍然是一个非常有用的工具. 在行尾加 // NOLINT, 或在上一行加 // NOLINTNEXTLINE, 可以忽略报错.
某些项目会指导你如何使用他们的项目工具运行 cpplint.py. 如果你参与的项目没有提供, 你可以单独下载 cpplint.py.
http://github.com/google/styleguide/blob/gh-pages/cpplint/cpplint.py
六、其他 C++ 特性
1.缺省函数
我们不允许使用缺省函数参数,少数极端情况除外。尽可能改用函数重载。
2.变长数组和 alloca()
我们不允许使用变长数组和 alloca().
3.异常
我们不使用 C++ 异常
4.类型转换
使用 C++ 的类型转换, 如 static_cast<>().
不要使用 int y = (int)x 或 int y = int(x) 等转换方式;
5.流
只在记录日志时使用流.
流用来替代 printf() 和 scanf().
6.前置自增和自减
对于迭代器和其他模板对象使用前缀形式 (++i) 的自增, 自减运算符.
对于变量在自增 (++i 或 i++) 或自减 (–i 或 i–) 后表达式的值又没有没用到的情况下, 需要确定到底是使用前置还是后置的自增 (自减).
7.const 用法
我们强烈建议你在任何可能的情况下都要使用 const. 此外有时改用 C++11 推出的 constexpr 更好。
8.constexper 用法
在 C++11 里,用 constexpr 来定义真正的常量,或实现常量初始化。
9.整型
C++ 内建整型中, 仅使用 int. 如果程序中需要不同大小的变量, 可以使用 <stdint.h> 中长度精确的整型, 如 int16_t.
如果变量可能不小于 2^31 (2GiB), 就用 64 位变量比如 int64_t.
此外要留意,哪怕值并不会超出 int 所能够表示的范围,在计算过程中也可能会溢出。所以拿不准时,干脆用更大的类型。
定义:
C++ 没有指定整型的大小. 通常人们假定 short 是 16 位, int 是 32 位, long 是 32 位, long long 是 64 位.
目的是为了保持声明统一
10.64位下的可移植性
代码应该对 64 位和 32 位系统友好.
11.预处理宏
使用宏时要非常谨慎, 尽量以内联函数, 枚举和常量代替之.
12. sizeof
尽可能用 sizeof(varname) 代替 sizeof(type).
使用 sizeof(varname) 是因为当代码中变量类型改变时会自动更新. 您或许会用 sizeof(type) 处理不涉及任何变量的代码,比如处理来自外部或内部的数据格式,这时用变量就不合适了。
13.auto
用 auto 绕过烦琐的类型名,只要可读性好就继续用,别用在局部变量之外的地方。
14.模板编程
不要使用复杂的模板编程.
定义:
模板编程指的是利用c++ 模板实例化机制是图灵完备性, 可以被用来实现编译时刻的类型判断的一系列编程技巧.
15.Boost 库
只使用 Boost 中被认可的库.
定义:
Boost 库集 是一个广受欢迎, 经过同行鉴定, 免费开源的 C++ 库集.
16.C++11
适当用 C++11(前身是 C++0x)的库和语言扩展,在贵项目用 C++11 特性前三思可移植性。
定义:
C++11 有众多语言和库上的 变革 。
七、命名的约定
1.通用命名规则
函数命名, 变量命名, 文件命名要有描述性; 少用缩写.
尽可能使用描述性的命名, 别心疼空间, 毕竟相比之下让代码易于新读者理解更重要. 不要用只有项目开发者能理解的缩写, 也不要通过砍掉几个字母来缩写单词.
注意, 一些特定的广为人知的缩写是允许的, 例如用 i 表示迭代变量和用 T 表示模板参数.
2.文件命名
文件名要全部小写, 可以包含下划线 () 或连字符 (-), 依照项目的约定. 如果没有约定, 那么 “” 更好.
可接受的文件命名示例:
my_useful_class.cc
my-useful-class.cc
myusefulclass.cc
myusefulclass_test.cc // _unittest 和 _regtest 已弃用.
C++ 文件要以 .cc 结尾, 头文件以 .h 结尾. 专门插入文本的文件则以 .inc 结尾.
不要使用已经存在于 /usr/include 下的文件名 (Yang.Y 注: 即编译器搜索系统头文件的路径), 如 db.h.
通常应尽量让文件名更加明确.
内联函数定义必须放在 .h 文件中. 如果内联函数比较短, 就直接将实现也放在 .h 中.
3.类型命名
类型名称的每个单词首字母均大写, 不包含下划线: MyExcitingClass, MyExcitingEnum.
4.变量命名
变量 (包括函数参数) 和数据成员名一律小写, 单词之间用下划线连接.
类的成员变量以下划线结尾, 但结构体的就不用, 如: a_local_variable, a_struct_data_member, a_class_data_member_.
类数据成员:
不管是静态的还是非静态的, 类数据成员都可以和普通变量一样, 但要接下划线.
结构体变量:
不管是静态的还是非静态的, 结构体数据成员都可以和普通变量一样, 不用像类那样接下划线
5.常量命名
声明为 constexpr 或 const 的变量, 或在程序运行期间其值始终保持不变的, 命名时以 “k” 开头, 大小写混合. 例如:
const int kDaysInAWeek = 7;
所有具有静态存储类型的变量 (例如静态变量或全局变量) 都应当以此方式命名. 对于其他存储类型的变量, 如自动变量等, 这条规则是可选的. 如果不采用这条规则, 就按照一般的变量命名规则.
6.函数命名
常规函数使用大小写混合, 取值和设值函数则要求与变量名匹配: MyExcitingFunction(), MyExcitingMethod(), my_exciting_member_variable(), set_my_exciting_member_variable().
一般来说, 函数名的每个单词首字母大写 (即 “驼峰变量名” 或 “帕斯卡变量名”), 没有下划线. 对于首字母缩写的单词, 更倾向于将它们视作一个单词进行首字母大写 (例如, 写作 StartRpc() 而非 StartRPC()).
7.命名空间命名
命名空间以小写字母命名.
最高级命名空间的名字取决于项目名称.
要注意避免嵌套命名空间的名字之间和常见的顶级命名空间的名字之间发生冲突.
8.枚举命名
枚举的命名应当和 常量 或 宏 一致: kEnumName 或是 ENUM_NAME.
单独的枚举值应该优先采用 常量 的命名方式. 但 宏 方式的命名也可以接受. 枚举名 UrlTableErrors (以及 AlternateUrlTableErrors) 是类型, 所以要用大小写混合的方式.
9.宏命名
你并不打算 使用宏, 对吧? 如果你一定要用, 像这样命名: MY_MACRO_THAT_SCARES_SMALL_CHILDREN.
10.命名规则的特例
如果你命名的实体与已有 C/C++ 实体相似, 可参考现有命名策略.
bigopen(): 函数名, 参照 open() 的形式
uint: typedef
bigpos: struct 或 class, 参照 pos 的形式
sparse_hash_map: STL 型实体; 参照 STL 命名约定
LONGLONG_MAX: 常量, 如同 INT_MAX
八、注释
1.注释风格
使用 // 或 /**/,统一就好
2.文件注释
在每一个文件开头加入版权公告.
文件注释描述了该文件的内容. 如果一个文件只声明, 或实现, 或测试了一个对象, 并且这个对象已经在它的声明处进行了详细的注释, 那么就没必要再加上文件注释. 除此之外的其他文件都需要文件注释.
每个文件都应该包含许可证引用. 为项目选择合适的许可证版本.(比如, Apache 2.0, BSD, LGPL, GPL)
如果你对原始作者的文件做了重大修改, 请考虑删除原作者信息.
3.类注释
每个类的定义都要附带一份注释, 描述类的功能和用法, 除非它的功能相当明显.
4.函数注释
函数声明处的注释描述函数功能;
定义处的注释描述函数实现.
函数声明处注释的内容:
- 函数的输入输出.
- 对类成员函数而言: 函数调用期间对象是否需要保持引用参数, 是否会释放这些参数.
- 函数是否分配了必须由调用者释放的空间.
- 参数是否可以为空指针.
- 是否存在函数使用上的性能隐患.
- 如果函数是可重入的, 其同步前提是什么?
5.变量注释
通常变量名本身足以很好说明变量用途. 某些情况下, 也需要额外的注释说明.
6.实现注释
对于代码中巧妙的, 晦涩的, 有趣的, 重要的地方加以注释.
- 代码前注释: 巧妙或复杂的代码段前要加注释.
- 行注释: 比较隐晦的地方要在行尾加入注释. 在行尾空两格进行注释.
7.标点,拼写和语法
注意标点, 拼写和语法; 写的好的注释比差的要易读的多.
8.TODO 注释
对那些临时的, 短期的解决方案, 或已经够好但仍不完美的代码使用 TODO 注释.
// TODO(kl@gmail.com): Use a "*" here for concatenation operator.
// TODO(Zeke) change this to use relations.
// TODO(bug 12345): remove the "Last visitors" feature
9.弃用注释
通过弃用注释(DEPRECATED comments)以标记某接口点已弃用.
您可以写上包含全大写的 DEPRECATED 的注释, 以标记某接口为弃用状态. 注释可以放在接口声明前, 或者同一行.
在 DEPRECATED 一词后, 在括号中留下您的名字, 邮箱地址以及其他身份标识.
九、格式
1.行长度
每一行代码字符数不超过 80.
2.非 ASCII 字符
尽力不使用非 ASCII 字符,使用时必须使用 UTF-8 编码
3.空格还是制表符
只使用空格, 每次缩进 2 个空格.
4.函数声明与定义
返回类型和函数名在同一行, 参数也尽量放在同一行, 如果放不下就对形参分行, 分行方式与 函数调用 一致.
5.Lambda 表达式
Lambda 表达式对形参和函数体的格式化和其他函数一致; 捕获列表同理, 表项用逗号隔开.
6.函数调用
要么一行写完函数调用, 要么在圆括号里对参数分行, 要么参数另起一行且缩进四格. 如果没有其它顾虑的话, 尽可能精简行数, 比如把多个参数适当地放在同一行里.
7.列表初始化格式
您平时怎么格式化函数调用, 就怎么格式化 列表初始化.
8.条件语句
倾向于不在圆括号内使用空格. 关键字 if 和 else 另起一行.
注意所有情况下 if 和左圆括号间都有个空格. 右圆括号和左大括号之间也要有个空格
9.循环和开关选择语句
switch 语句可以使用大括号分段, 以表明 cases 之间不是连在一起的. 在单语句循环里, 括号可用可不用. 空循环体应使用 {} 或 continue.
10.指针和引用表达式
句点或箭头前后不要有空格. 指针/地址操作符 (*, &) 之后不能有空格.
x = *p;
p = &x;
x = r.y;
x = r->y;
11.布尔表达式
如果一个布尔表达式超过 标准行宽, 断行方式要统一一下.
12.函数返回值
不要在 return 表达式里加上非必须的圆括号.
13.变量及数组初始化
用 =, () 和 {} 均可.
14.预处理指令
预处理指令不要缩进, 从行首开始.
15.类格式
访问控制块的声明依次序是 public:, protected:, private:, 每个都缩进 1 个空格.
16.构造函数初始值列表
构造函数初始化列表放在同一行或按四格缩进并排多行.
17.命名空间格式化
命名空间内容不缩进.
18.水平留白
水平留白的使用根据在代码中的位置决定. 永远不要在行尾添加没意义的留白.
void f(bool b) { // 左大括号前总是有空格.
...
int i = 0; // 分号前不加空格.
// 列表初始化中大括号内的空格是可选的.
// 如果加了空格, 那么两边都要加上.
int x[] = { 0 };
int x[] = {0};
// 继承与初始化列表中的冒号前后恒有空格.
class Foo : public Bar {
public:
// 对于单行函数的实现, 在大括号内加上空格
// 然后是函数实现
Foo(int b) : Bar(), baz_(b) {} // 大括号里面是空的话, 不加空格.
void Reset() { baz_ = 0; } // 用空格把大括号与实现分开.
...
if (b) { // if 条件语句和循环语句关键字后均有空格.
} else { // else 前后有空格.
}
while (test) {} // 圆括号内部不紧邻空格.
switch (i) {
for (int i = 0; i < 5; ++i) {
switch ( i ) { // 循环和条件语句的圆括号里可以与空格紧邻.
if ( test ) { // 圆括号, 但这很少见. 总之要一致.
for ( int i = 0; i < 5; ++i ) {
for ( ; i < 5 ; ++i) { // 循环里内 ; 后恒有空格, ; 前可以加个空格.
switch (i) {
case 1: // switch case 的冒号前无空格.
...
case 2: break; // 如果冒号有代码, 加个空格.
// 赋值运算符前后总是有空格.
x = 0;
// 其它二元操作符也前后恒有空格, 不过对于表达式的子式可以不加空格.
// 圆括号内部没有紧邻空格.
v = w * x + y / z;
v = w*x + y/z;
v = w * (x + z);
// 在参数和一元操作符之间不加空格.
x = -5;
++x;
if (x && !y)
...
// 尖括号(< and >) 不与空格紧邻, < 前没有空格, > 和 ( 之间也没有.
vector<string> x;
y = static_cast<char*>(x);
// 在类型与指针操作符之间留空格也可以, 但要保持一致.
vector<char *> x;
19.垂直留白
垂直留白越少越好.

被折叠的 条评论
为什么被折叠?



