浮点数的二进制表示学习笔记

 

基础知识:

十进制转十六进制;

十六进制转二进制;

IEEE制定的浮点数表示规则;

了解:

目前C/C++编译器标准都遵照IEEE制定的浮点数表示法来进行float,double运算。这种结构是一种科学计数法,用符号、指数和尾数来表示,底数定为2——即把一个浮点数表示为尾数乘以2的指数次方再添上符号。下面是具体的规格:
             符号位     阶码      尾数     长度
float           1          8        23      32
double          1         11        52      64

 

以下通过几个例子讲解浮点数如何转换为二进制数

例一:

已知:double类型38414.4

求:其对应的二进制表示。

分析:double类型共计64位,折合8字节。由最高到最低位分别是第636261……0位:
    最高位63位是符号位,1表示该数为负,0表示该数为正;
    62-52位,一共11位是指数位;
    51-0位,一共52位是尾数位。

     步骤:按照IEEE浮点数表示法,下面先把38414.4转换为十六进制数。
     把整数部和小数部分开处理:整数部直接化十六进制:960E。小数的处理:
0.4=0.5*0+0.25*1+0.125*1+0.0625*0+……
     
实际上这永远算不完!这就是著名的浮点数精度问题。所以直到加上前面的整数部分算够53位就行了。隐藏位技术:最高位的1不写入内存(最终保留下来的还是52位)。
    如果你够耐心,手工算到53位那么因该是:38414.4(10)=1001011000001110.
0110011001100110011001100110011001100(2)

科学记数法为:1.001011000001110 0110011001100110011001100110011001100,右移了15位,所以指数为15。或者可以如下理解:

1.001011000001110 0110011001100110011001100110011001100×2^15
     
于是来看阶码,按IEEE标准一共11位,可以表示范围是-1024 ~ 1023。因为指数可以为负,为了便于计算,规定都先加上1023(2^10-1),在这里,阶码:15+1023=1038。二进制表示为:100 00001110
    符号位:因为38414.4为正对应 0
    合在一起(注:尾数二进制最高位的1不要):
01000000 11100010 11000001 110
 01100  11001100  11001100  11001100  11001100

 

例二:

已知:整数3490593(16进制表示为0x354321)

求:其对应的浮点数3490593.0的二进制表示。 

解法如下:

先求出整数3490593的二进制表示:

 H:    3     5    4    3    2     1   (十六进制表示)

 B:   0011  0101 0100 0011 0010  0001 (二进制表示)

        │←──────21─────→│

 

即: 

               1.1010101000011001000012×221

可见,从左算起第一个121位,我们将这21为作为浮点数的小数表示,单精度浮点数float由符号位1位,指数域位k=8位,小数域位(尾数)n=23位构成,因此对上面得到的21位小数位我们还需要补上20,得到浮点数的小数域表示为:

         1 0101 0100 0011 0010 0001 00

 

float类型的偏置量Bias=2k-1-1=28-1-1=127,但还要补上刚才因为右移作为小数部分的21位,因此偏置量为127+21=148,就是IEEE浮点数表示标准:

                          V = (-1)s×M×2E

                    E = e-Bias

中的e,此前计算Bias=127,刚好验证了E=148-127=21

 

148转为二进制表示为10010100,加上符号位0,最后得到二进制浮点数表示1001010010101010000110010000100,其16进制表示为:

 H:     4        A       5          5         0         C         8        4  

 B:  0100   1010   0101    0101   0000   1100  1000   0100

                    |←────      21        ─────→   |

     1|←─8   ─→||←─────       23       ─────→ |

 

这就是浮点数3490593.0(0x4A550C84)的二进制表示。

 

例三:

0.5的二进制形式是0.1

它用浮点数的形式写出来是如下格式

 

0                01111110                 00000000000000000000000


符号位           阶码                       小数位

正数符号位为0,负数符号位为1

阶码是以2为底的指数

小数位表示小数点后面的数字


下面我们来分析一下0.5是如何写成0 01111110 00000000000000000000000


首先0.5是正数所以符号位为0

再来看阶码部分,0.5的二进制数是0.1,0.11.0*2^(-1),所以我们总结出来:

要把二进制数变成(1.f)*2^(exponent)的形式,其中exponent是指数

而由于阶码有正负之分所以阶码=127+exponent;

即阶码=127+(-1)=126  01111110

余下的小数位为二进制小数点后面的数字,00000000000000000000000


由以上分析得0.5的浮点数存储形式为0 01111110 00000000000000000000000  

注:如果只有小数部分,那么需要右移小数点比如右移3位才能放到第一个1的后面阶码就是127-3=124.

例四   (20.59375)10 =(10100.10011 2

首先分别将整数和分数部分转换成二进制数: 

20.59375
10100.10011 

然后移动小数点,使其在第12位之间 

10100.10011
1.010010011×2^4   e

于是得到: 

S
0 E4127131 M010010011 

最后得到32位浮点数的二进制存储格式为: 

0100 1001 1010 0100 1100 0000 0000 0000
(41A4C000)16

 

 

例五:
-12.5转为单精度二进制表示
12.5: 
1. 
整数部分12,二进制为1100; 小数部分0.5, 二进制是.1,先把他们连起来,从第一个1数起取24位(后面补0): 
1100.10000000000000000000 

这部分是有效数字。(把小数点前后两部分连起来再取掉头前的1,就是尾数) 
2. 
把小数点移到第一个1的后面,需要左移3位(1.10010000000000000000000*2^3加上偏移量127127+3=130,二进制是10000010,这是阶码。 
3. -12.5
是负数,所以符号位是1。把符号位,阶码和尾数连起来。注意,尾数的第一位总是1,所以规定不存这一位的1,只取后23位: 
1 10000010 10010000000000000000000 

把这32位按8位一节整理一下,得: 
11000001 01001000 00000000 00000000 

就是十六进制的 C1480000. 

例六:

2.025675 
1. 
整数部分2,二进制为10; 小数部分0.025675, 二进制是.0000011010010010101001,先把他们连起来,从第一个1数起取24位(后面补0): 
10.0000011010010010101001 

这部分是有效数字。把小数点前后两部分连起来再取掉头前的1,就是尾数: 00000011010010010101001 
2. 
把小数点移到第一个1的后面,左移了1加上偏移量127127+1=128,二进制是10000000,这是阶码。
3. 2.025675是正数,所以符号位是0。把符号位,阶码和尾数连起来: 
0 10000000 00000011010010010101001 

把这32位按8位一节整理一下,得: 
01000000 00000001 10100100 10101001 

就是十六进制的 4001A4A9.  


 

例七:
(逆向求十进制整数)一个浮点二进制数手工转换成十进制数的例子: 
假设浮点二进制数是 1011 1101 0100 0000 0000 0000 0000 0000 
1823位分成三段: 
01111010 10000000000000000000000 
最后一段是尾数。前面加上"1.", 就是 1.10000000000000000000000 
下面确定小数点位置。由
E = e-Bias阶码E01111010,加上00000101才是01111111127), 
所以他减去127的偏移量得e=-5。(或者化成十进制得122122-127=-5)。 
因此尾数1.10(后面的0不写了)是小数点右移5位的结果。要复原它就要左移5位小数点,得0.0000110, 即十进制的0.046875 
最后是符号:1代表负数,所以最后的结果是 -0.046875 

注意:其他机器的浮点数表示方法可能与此不同不能任意移植

 

再看一例(类似例七)

比如:53004d3e

二进制表示为:

01010011000000000100110100111110

按照1个符号    8个指数          23个小数位划分

0              10100110         00000000100110100111110

正确的结果转出来应该是551051722752.0

该怎么算?

好,我们根据IEEE的浮点数表示规则划分,得到这个浮点数的小数位是:

 00000000100110100111110

那么它的二进制表示就应该是:


1.000000001001101001111102 × 239

这是怎么来的呢? 别急,听我慢慢道来。
标准化公式中的M要求在规格化的情况下,取值范围1<M<(2-ε)

正因为如此,我们才需要对原始的整数二进制表示做偏移,偏移多少呢?偏移2E
这个“E”怎么算?上面的239怎么得来的呢?浮点数表示中的8位指数为就是告诉这个的。我们知道:
E = e-Bias
那么根据指数位:

101001102=>16610
e=166,由此算出E=e-Bias=166-127=39,就是说将整数二进制表示转为标准的浮点数二进制表示的时候需要将小数点左移39位,好,我们现在把它还原得到整数的二进制表示:

1 00000000100110100111110 0000000000000000

1│←───── 23─────→│←─── 16───→│

23+16=39,后面接着就是小数点了。
拿出计算器,输入二进制数1000000001001101001111100000000000000000
转为十进制数,不正是:551051722752么!

通过这例六例七,介绍了将整数二进制表示转浮点数二进制表示的逆过程,还是希望大家不但能掌握转化的方法,更要理解转化的基本原理。

 

http://blog.163.com/yql_bl/blog/static/847851692008112013117685/

尾递归与Continuation

 

 

关于递归操作,相信大家都已经不陌生。简单地说,一个函数直接或间接地调用自身,是为直接或间接递归。例如,我们可以使用递归来计算一个单向链表的长度:

 

public class Node
{
    public Node(int value, Node next)
    {
        this.Value = value;
        this.Next = next;
    }

public int Value { get; private set; }

public Node Next { get; private set; }
}

编写一个递归的GetLength方法:

public static int GetLengthRecursively(Node head)
{
    if (head == null) return 0;
    return GetLengthRecursively(head.Next) + 1;
}

在调用时,GetLengthRecursively方法会不断调用自身,直至满足递归出口。对递归有些了解的朋友一定猜得到,如果单项链表十 分长,那么上面这个方法就可能会遇到栈溢出,也就是抛出StackOverflowException。这是由于每个线程在执行代码时,都会分配一定尺寸 的栈空间(Windows系统中为1M),每次方法调用时都会在栈里储存一定信息(如参数、局部变量、返回地址等等),这些信息再少也会占用一定空间,成 千上万个此类空间累积起来,自然就超过线程的栈空间了。不过这个问题并非无解,我们只需把递归改成如下形式即可(在这篇文章里我们不考虑非递归的解法):

public static int GetLengthTailRecursively(Node head, int acc)
{
    if (head == null) return acc;
    return GetLengthTailRecursively(head.Next, acc + 1);
}

GetLengthTailRecursively方法多了一个acc参数,acc的为accumulator(累加器)的缩写,它的功能是在 递归调用时“积累”之前调用的结果,并将其传入下一次递归调用中——这就是GetLengthTailRecursively方法与 GetLengthRecursively方法相比在递归方式上最大的区别:GetLengthRecursive方法在递归调用后还需要进行一次 “+1”,而GetLengthTailRecursively的递归调用属于方法的最后一个操作。这就是所谓的“尾递归”。与普通递归相 比,由于 递归的调用处于方法的最后,因此方法之前所积累下的各种状态对于递归调用结果已经没有任何意义,因此完全可以把本次方法中留在堆栈中的 数据完全清除,把空间让给最后的递归调用。这样的优化1便使得递归不会在调用堆栈上产生堆积,意味着即时是“无限”递归也不会让堆 栈溢出。这便是尾递归的优势。

有些朋友可能已经想到了,尾递归的本质,其实是将递归方法中的需要的“所有状态”通过方法的参数传入下一次调用中。对于 GetLengthTailRecursively方法,我们在调用时需要给出acc参数的初始值:

GetLengthTailRecursively(head, 0)

为了进一步熟悉尾递归的使用方式,我们再用著名的“菲波纳锲”数列作为一个例子。传统的递归方式如下:

public static int FibonacciRecursively(int n)
{
    if (n < 2) return n;
    return FibonacciRecursively(n - 1) + FibonacciRecursively(n - 2);
}

而改造成尾递归,我们则需要提供两个累加器:

public static int FibonacciTailRecursively(int n, int acc1, int acc2)
{
    if (n == 0) return acc1;
    return FibonacciTailRecursively(n - 1, acc2, acc1 + acc2);
}

于是在调用时,需要提供两个累加器的初始值:

FibonacciTailRecursively(10, 0, 1)

尾 递归与Continuation

Continuation即为“完成某件事情”之后“还需要做的事情”。例如,在.NET中标准的APM调用方式,便是由BeginXXX方法和EndXXX方法构成,这其实便是 一种Continuation:在完成了BeginXXX方法之后,还需要调用EndXXX方法。而这种做法,也可以体现在尾递归 造中。例如以下为阶乘方法的传统递归定义:

public static int FactorialRecursively(int n)
{
    if (n == 0) return 1;
    return FactorialRecursively(n - 1) * n;
}

显然,这不是一个尾递归的方式,当然我们轻易将其转换为之前提到的尾递归调用方式。不过我们现在把它这样“理解”: 每次计算n的阶乘时,其实是“先获取n – 1的阶乘”之后再“与n相乘并返回”,于是我们的FactorialRecursively方法可以改造成:

public static int FactorialRecursively(int n)
{
    return FactorialContinuation(n - 1, r => n * r);
}

// 6. FactorialContinuation(n, x => x)
public static int FactorialContinuation(int n, Func<int, int> continuation)
{
    ...
}

FactorialContinuation方法的含义是“计算n的阶乘,并将结果传入continuation方法,并返回其调用结果”。于 是,很容易得出,FactorialContinuation方法自身便是一个递归调用:

public static int FactorialContinuation(int n, Func<int, int> continuation)
{
    return FactorialContinuation(n - 1,
        r => continuation(n * r));
}

FactorialContinuation方法的实现可以这样表述:“计算n的阶乘,并将结果传入continuation方法并返回”,也就是“计算n – 1的阶乘,并将结果与n相乘,再调用continuation方法”。为了实现“并将结果与n相乘,再调用continuation方法”这个逻辑,代码 又构造了一个匿名方法,再次传入FactorialContinuation方法。当然,我们还需要为它补充递归的出口条件:

public static int FactorialContinuation(int n, Func<int, int> continuation)
{
    if (n == 0) return continuation(1);
    return FactorialContinuation(n - 1,
        r => continuation(n * r));
}

很明显,FactorialContinuation实现了尾递归。如果要计算n的阶乘,我们需要如下调用 FactorialContinuation方法,表示“计算10的阶乘,并将结果直接返回”:

FactorialContinuation(10, x => x)

再加深一下印象,大家是否能够理解以下计算“菲波纳锲”数列第n项值的写法?

public static int FibonacciContinuation(int n, Func<int, int> continuation)
{
    if (n < 2) return continuation(n);
    return FibonacciContinuation(n - 1,
        r1 => FibonacciContinuation(n - 2,
            r2 => continuation(r1 + r2)));
}

在函数式编程中,此类调用方式便形成了“Continuation Passing Style(CPS)”。由于C#的Lambda表达式能够轻松构成一个匿名方法,我们也可以在C#中实现这样的调用方式。您 可能会想——汗,何必搞得这么复杂,计算阶乘和“菲波纳锲”数列不是一下子就能转换成尾递归形式的吗?不过,您试试看以下的例子呢?

对二叉树进行先序遍历(pre-order traversal)是典型的递归操作,假设有如下TreeNode类:

public class TreeNode
{
    public TreeNode(int value, TreeNode left, TreeNode right)
    {
        this.Value = value;
        this.Left = left;
        this.Right = right;
    }

public int Value { get; private set; }

public TreeNode Left { get; private set; }

public TreeNode Right { get; private set; }
}

于是我们来传统的先序遍历一下:

public static void PreOrderTraversal(TreeNode root)
{
    if (root == null) return;

Console.WriteLine(root.Value);
    PreOrderTraversal(root.Left);
    PreOrderTraversal(root.Right);
}

您能用“普通”的方式将它转换为尾递归调用吗?这里先后调用了两次PreOrderTraversal,这意味着必然有一 次调用没法放在末尾。这时候便要利用到Continuation了:

public static void PreOrderTraversal(TreeNode root, Action<TreeNode> continuation)
{
    if (root == null)
    {
        continuation(null);
        return;
    }

Console.WriteLine(root.Value);

PreOrderTraversal(root.Left,
        left => PreOrderTraversal(root.Right,
            right => continuation(right)));
}

我们现在把每次递归调用都作为代码的最后一次操作,把接下来的操作使用Continuation包装起来,这样就实现了尾递归避免了堆栈数据的堆积。可见,虽然使用Continuation是一个略有些“诡异”的使用方式,但是在某些时候它也是必不可少的使用技巧。

Continuation的改进

看看刚才的先序遍历实现,您有没有发现一个有些奇怪的地方?

PreOrderTraversal(root.Left,
    left => PreOrderTraversal(root.Right,
        right => continuation(right)));

关于最后一步,我们构造了一个匿名函数作为第二次PreOrderTraversal调用的Continuation,但是其内部直接调用了 continuation参数——那么我们为什么不直接把它交给第二次调用呢?如下:

PreOrderTraversal(root.Left,
    left => PreOrderTraversal(root.Right, continuation));

我们使用Continuation实现了尾递归,其实是把原本应该分配在栈上的信息丢到了托管堆上。每个匿名方法其实都是托管堆上 的对象,虽然说这种生存周期短的对象不会对内存资源方面造成多大问题,但是尽可能减少此类对象,对于性能肯定是有帮助的。这里再举一个更为明显的例子,求 二叉树的大小(Size):

public static int GetSize(TreeNode root, Func<int, int> continuation)
{
    if (root == null) return continuation(0);
    return GetSize(root.Left,
        leftSize => GetSize(root.Right,
            rightSize => continuation(leftSize + rightSize + 1)));
}

GetSize方法使用了Continuation,它的理解方法是“获取root的大小,再将结果传入continuation,并返回其调 用结果”。我们可以将其进行改写,减少Continuation方法的构造次数:

public static int GetSize2(TreeNode root, int acc, Func<int, int> continuation)
{
    if (root == null) return continuation(acc);
    return GetSize2(root.Left, acc,
        accLeftSize => GetSize2(root.Right, accLeftSize + 1, continuation));
}

GetSize2方法多了一个累加器参数,同时它的理解方式也有了变化:“将root的大小累加到acc上,再将结果传入 continuation,并返回其调用结果”。也就是说GetSize2返回的其实是一个累加值,而并非是root参数的实际尺寸。当然,我们在调用时 GetSize2时,只需将累加器置零便可:

GetSize2(root, 0, x => x)

不知您清楚了吗?

结束

在命令式编程中,我们解决一些问题往往可以使用循环来代替递归,这样便不会因为数据规模造成堆栈溢出。但是在函数式编程中,要实现“循环”的唯 一方法便是“递归”,因此尾递归CPS对于函数式编程的意义非常重大。了解尾递归,对于编程思维也有很大帮助,因此大家不妨多 加思考和练习,让这样的方式为自己所用。

注1:事实上,在C#中,即使您实现了尾递归,编译器(包括C#编译器及JIT)也不会进行优化,也就是说还是无法避免 StackOverflowException

 

源文档 <http://blog.201314.info/2010/05/24/%E5%B0%BE%E9%80%92%E5%BD%92%E4%B8%8Econtinuation.html>

一些重要的算法

 

一些重要的算法

酷壳 - CoolShell.cn 作者:陈皓

有超过 100 人喜欢此条目

下面是一些比较重要的算法,原文列了32个,但我觉得有很多是数论里的,和计算机的不相干,所以没有选取。下面的这些,有的我们经常在用,有的基本不用。有的很常见,有的很偏。不过了解 一下也是好事。也欢迎你留下你觉得有意义的算法。(注:本篇文章并非翻译,其中的算法描述大部份摘自Wikipedia,因为维基百科描述的很专业了)

  1. A*搜寻算法
    俗称A星算法。这是一种在图形平面上,有多个节点的路径,求出最低通过成本的算法。常用于游戏中的NPC的移动计算,或线上游戏的BOT的移动计算上。该算法像Dijkstra算法一样,可以找到一条最短路径;也像BFS一样,进行启发式的搜索。
  2. Beam Search
    束搜索(beam search) 方法是解决优化问题的一种启发式方法,它是在分枝定界方法基础上发展起来的,它使用启发式方法估计k 个最好的路径,仅从这k 个路径出发向下搜索,即每一层只有满意的结点会被保留,其它的结点则被永久抛弃,从而比分枝定界法能大大节省运行时间。束搜索于20 世纪70 年代中期首先被应用于人工智能领域,1976 Lowerre 在其称为HARPY的语音识别系统中第一次使用了束搜索方法,他的目标是并行地搜索几个潜在的最优决策路径以减少回溯,并快速地获得一个解。
  3. 二分取中查找算法
    一种在有序数组中查找某一特定元素的搜索算法。搜素过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜素过程结束;如果某一特定元素大于或 者小于中间元素,则在数组大于或小于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较。这种搜索算法每一次比较都使搜索范围缩小一半。
  4. Branch and bound
    分支定界 (branch and bound) 算法是一种在问题的解空间树上搜索问题的解的方法。但与回溯算法不同,分支定界算法采用广度优先或最小耗费优先的方法搜索解空间树,并且,在分支定界算法中,每一个活结点只有一次机会成为扩展结点。
  5. 数据压缩
    数据压缩是通过减少计算机中所存储数据或者通信传播中数据的冗余度,达到增大数据密度,最终使数据的存储空间减少的技术。数据压缩在文件存储和分布式系统领域有着十分广泛的应用。数据压缩也代表着尺寸媒介容量的增大和网络带宽的扩展。
  6. Diffie–Hellman密钥协商
    Diffie–Hellman key exchange,简称“D–H” 是一种安全协议。它可以让双方在完全没有对方任何预先信息的条件下通过不安全信道建立起一个密钥。这个密钥可以在后续的通讯中作为对称密钥来加密通讯内容。
  1. Dijkstra’s 算法
    迪科斯彻算法(Dijkstra)是由荷兰计算机科学家艾兹格·迪科斯彻(Edsger Wybe Dijkstra)发明的。算法解决的是有向图中单个源点到其他顶点的最短路径问题。举例来说,如果图中的顶点表示城市,而边上的权重表示著城市间开车行经的距离,迪科斯彻算法可以用来找到两个城市之间的最短路径。
  1. 动态规划
    动态规划是一种在数学和计算机科学中使用的,用于求解包含重叠子问题的最优化问题的方法。其基本思想是,将原问题分解为相似的子问题,在求解的过程中通过子问题的解求出原问题的解。动态规划的思想是多种算法的基础,被广泛应用于计算机科学和工程领域。比较著名的应用实例有:求解最短路径问题,背包问题项目管理网络流优化等。这里也有一篇文章说得比较详细。
  1. 欧几里得算法
    在数学中,辗转相除法,又称欧几里得算法,是求最大公约数的算法。辗转相除法首次出现于欧几里得的《几何原本》(第VII卷,命题i和ii)中,而在中国则可以追溯至东汉出现的《九章算术》。
  1. 最大期望(EM)算法
    在统计计算中,最大期望(EM)算法是在概率probabilistic)模型中寻找参数最大似然估计的算法,其中概率模型依赖于无法观测的隐藏变量(Latent Variable)。最大期望经常用在机器学习计算机视觉数据聚类Data Clustering领域。最大期望算法经过两个步骤交替进行计算,第一步是计算期望(E),利用对隐藏变量的现有估计值,计算其最大似然估计值;第二步是最大化(M),最大 化在 E 步上求得的最大似然值来计算参数的值。M 步上找到的参数估计值被用于下一个 E 步计算中,这个过程不断交替进行。
  1. 快速傅里叶变换 (FFT)
    快速傅里叶变换(Fast Fourier Transform,FFT),是离散傅里叶变换的快速算法,也可用于计算离散傅里叶变换的逆变换。快速傅里叶变换有广泛的应用,如数字信号处理、计算大整数乘法、求解偏微分方程等等。本条目只描述各种快速算法,对于离散傅里叶变换的性质和应用,请参见离散傅里叶变换
  1. 哈希函数
    Hash Function是一种从任何一种数据中创建小的数字“指纹”的方法。该函数将数据打乱混合,重新创建一个叫做散列值的指纹。散列值通常用来代表一个短的 随机字母和数字组成的字符串。好的散列函数在输入域中很少出现散列冲突。在散列表和数据处理中,不抑制冲突来区别数据,会使得数据库记录更难找到。
  1. 堆排序
    Heapsort
    是指利用堆积树)这种数据结构所设计的一种排序算法。堆积树是一个近似完全二叉树的结构,并同时满足堆积属性:即子结点的键值或索引总是小于(或者大于)它的父结点。
  1. 归并排序
    Merge sort
    是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。
  1. RANSAC 算法
    RANSAC 是”RANdom SAmple Consensus”的缩写。该算法是用于从一组观测数据中估计数学模型参数的迭代方法,由Fischler and Bolles1981 提出,它是一种非确定性算法,因为它只能以一定的概率得到合理的结果,随着迭代次数的增加,这种概率是增加的。 该算法的基本假设是观测数据集中存在”inliers”(那些对模型参数估计起到支持作用的点)和”outliers”(不符合模型的点),并且这组观测 数据受到噪声影响。RANSAC 假设给定一组”inliers”数据就能够得到最优的符合这组点的模型。
  1. RSA加密演算法
    这是一个公钥加密算法,也是世界上第一个适合用来做签名的算法。今天的RSA已经专利失效,其被广泛地用于电子商务加密,大家都相信,只要密钥足够长,这个算法就会是安全的
  1. 并查集Union-find
    并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。常常在使用中以森林来表示。
  1. Viterbi algorithm
    寻找最可能的隐藏状态序列(Finding most probable sequence of hidden states)

附录

 

 

源文档 <http://www.google.com/reader/view/>