本福德定律

2010年10月26日

本福德定律西蒙·纽科姆(Simon Newcomb)于1881年发现,弗兰克·本福德(Frank Benford)于1938年重新发现。该理论指出,对于许多以比例-变量方式出现的数字集,第一个数字是对数分布的,第一个数元1约占30%的时间,逐位递减,直到第一个数字为9约占5%的时间。以数学方式表示,前导数字d日∈ {1 …b条-1} 对于以基数表示的数字b条将有可能发生P(P)d日=对数b条(1 +1/d日). 因此,以10为基数,第一位数字是数字1、2、…9的概率为30.1%、17.6%、12.5%、9.7%、7.9%、6.7%、5.8%、5.1%和4.6%。

本福德定律是反直觉的,但在自然界中经常出现。它也经常发生用于审计列出过去一年中簿记员写的支票金额;如果超过5%是以数字8或9开头的,那么簿记员很可能是盗用公款的人。更重要的是,一年前有争议的伊朗选举的选区结果显示第一位数异常,建议投票舞弊.

最近,克里希纳穆提发表了编程挑战在Racket邮件列表上,要求最小/最紧/最干净/最好的代码来计算数字列表的第一位数百分比;他还要求读者将该函数应用于逗号分隔的值文件中的数据。他没有透露消息来源,但提到他对米塞索塔湖沿岸地区(以英亩计)感兴趣;示例数据显示在下一页.

您的第一个任务是编写一个函数,计算数字列表的第一位数百分比。第二个任务是计算下一页数据的第一位数百分比。完成后,欢迎您阅读运行建议的解决方案,或在下面的评论中发布自己的解决方案或讨论练习。

页:1 2

12对“本福德定律”的回应

  1. […]今天的编程实践练习,我们的任务是看看Benford定律(小写数字更多[…]

  2. My Haskell解决方案(请参阅http://bonsaicode.wordpress.com/2010/10/26/programming-praxis-benford%E2%80%99s-法律/对于带有注释的版本):

    导入数据。列表导入文本。打印第一位数字::[Float]->[(Char,Double)]firstDigits xs=map(\ds->(头ds,100*toEnum(长度ds))/toEnum(长度xs))。组。排序$map(head.show)xsshriram::[[String]]->[(Char,Double)]shriram xs=firstDigits[n|[(n,_)]<-map(读取。(!!3))xs]
  3. 格雷厄姆

    我直接跳过从csv文件中读取值,然后执行分析。我的Python解决方案:

    #!/usr/bin/env-python导入csvdef benford(csv文件,数字位置):"""计算numerical_position中第一个数字出现的百分比在有问题的csv文件中。"""r=csv.reader(打开(csv_file))d=dict(zip(范围(1,10),[0]*9))对于r中的行:尝试:d[int(行[numerical_position][0])]+=1除:通过总计=浮点(总和(d.值()))对于k in d:打印“%s:\t%.4s%%”%(k,100*d[k]/总计)返回如果__name__=='__main__':本福德('lakes.csv',3)

    在给定的csv文件(我将其命名为lakes.csv)上运行时,将生成:

    $ ./benford.py公司
    1: 32.0%
    2: 19.7%
    3: 12.7%
    4:9.08%
    5: 6.67%
    6: 6.14%
    7: 5.25%
    8: 4.45%
    9: 3.82%

  4. 格雷厄姆

    很抱歉我的输出发布混乱!

  5. 赌徒

    与乔·马歇尔的类似:

    (定义(第一位数n基数)
    ;;返回n的第一个基数
    (let((基数^2(*基数)))
    (秒((<n基数)n)
    ((<n基数^2)(商n基数)
    (否则
    (第一位(第一位n基数^2)基数))))

  6. 杀人

    一个ruby版本也将直接进行CSV计算…

    需要“csv”def benford(csv文件,位置)first_digits=数组.new(10,0)CSV.foreach(CSV_file)do|row|数字=行[位置].to_s[0]如果数字=~/[0-9],则first_digits[digit.to_i]+=1/结束第一位数字结束first_digits=benford(“lakes.csv”,3)total=first_digits.inject(0){|sum,v|sum+v}如果i!=,则first_digits.each_with_index{|v,i|将“#{i}#{(v.to_f/total.to_f)*100.0}的百分比”放入0 }
  7. Axio公司

    ;;自然->数字
    (定义(头部-数量n)
    (let loop((n n))(如果(<n 10)n(loop(商n 10))))

    ;;自然值列表->数字
    (定义(benford l)
    (let((res(使向量为10 0));;存储结果
    (计数0)
    (映射(λ(n);;递增每个看到的数字的计数器
    (let((i(head-of-num n)))
    (向量集!res i(+1(向量引用res i)))
    (设置!计数(+1计数))
    l)
    (地图(λ(i);;除以元素数
    (let((val(/(vector-ref res i)计数))
    (向量集!res i(列表i值(精确->不精确值)))
    (iota 09))
    res))

    (定义(测试数据)
    (本福德数据)

  8. Axio公司

    ;;我更喜欢这个版本。
    (定义(头部-数量n)
    (let loop((n n))(如果(<n 10)n(loop(商n 10))))

    (定义(benford l)
    (让*(计数0)
    (res(左折叠式
    (λ(n态)
    (let((i(head-of-num n)))
    (向量集!状态i(+1(向量引用状态i))
    (设置!计数(+1计数))
    状态))
    (品牌矢量10 0)
    l))
    (映射(λ(x)(精确->不精确(/x计数)))(矢量->列表res)))

    (定义(测试数据)
    (本福德数据)

  9. Khanh Nguyen先生

    我的F#代码

    //将单个数字列表划分到存储箱中让我分开=让rec拆分所有i=如果(i=9),则【ll】其他的让tt=列表分区(fun x->x=i)ll(fst-tt)::(split_aux(snd-tt)(i+1))split_aux l 0让rec first_digit x=如果x<10,则x为第一位数字(x/10)让benford l=let-first_digit_list=列表.map first_digit llet digits_counts=List.map List.length(拆分first_digit_List)//计算百分比List.map(fun x->(float x)/(float)(List.sum digits_counts))数字计数
  10. KNguyen公司

    我的F#实现:

    //将单个数字列表划分到存储箱中让我分开=让rec拆分所有i=如果(i=9),则【ll】其他的让tt=列表分区(fun x->x=i)ll(fst-tt)::(split_aux(snd-tt)(i+1))split_aux l 0让rec first_digit x=如果x<10,则x为第一位数字(x/10)让benford l=let-first_digit_list=列表.map first_digit llet digits_counts=List.map List.length(拆分first_digit_List)//计算百分比List.map(fun x->(float x)/(float)(List.sum digits_counts))数字计数
  11. 大卫

    然而,在FORTH中,我去掉了其中一个湖的引号中冒犯的逗号,而不是完全正确地解析csv…

    {--------------------拆分--------------------}{取自字符串库--------------}:(调整)(adr1 c1 adr2 ard3)2dup=如果在末尾2次丢弃0:0 2swap,则将余数设置为“”ELSE夹点2选取->r2向上r@1+/string\调整余数2swap下降r>\调整结果然后;:拆分(adr c delim--adr1 c1 adr2 c2)>r \保存分隔符2dup+2 pick \(adr c--adr c end adr adr)开始2dup>如果是dup c@r@<>则为假同时1+重复r>下降(调整);{--------------本福德--------------}可变样本大小创建数字10个单元格分配:第n个字段(adr c delim n--adr c)交换本地人|熟食|0 ?DO delim分体式2滴回路熟食二分熟;:get-digit(地址计数--数字)0>如果IF中的c@dup[char]1[char 9 1+]文字[字符]0-ELSE公司下降-1那么ELSE公司下降-1然后;:count-digit(地址计数--)获取digit dup 0>IF单元格数字+1交换+!1个样本大小+!ELSE公司然后;:init-beford(--)数字10单元格0填充0样本大小;:导入字段(n fd--)局部变量|输入n|BEGIN焊盘1024输入读线抛出WHILEpad swap[char],n个第n字段count-digitREPEAT下降;:四舍五入(n--n)10/mod掉期5+10/+;:.%(n-)10000个样本尺寸@*/四舍五入0<#[char]%持有#[char]。保持#s#>类型空间;:报告10 1完成铬i2.r。“:”i个单元格数字+@.percent回路;:benford(字段--)初始-边界bl字数r/o打开文件抛出2dup导入字段近距离抛投报告;

    执行:

    3 benford lakestats-mn.csv1: 32.1%2: 19.8%3: 12.7%4: 9.1%5: 6.7%6: 6.1%7: 5.3%8: 4.5%9:3.8%合格
  12. [……]约翰·库克(John D.Cook)是一位写数学的程序员(他可能会把自己描述成写编程的数学家),他最近写了一篇关于2的幂的前导数字的分布的文章,观察到它们遵循我们在前一个练习中研究过的本福德定律。[…]

留下评论