zoukankan      html  css  js  c++  java
  • 用欧拉计划学Rust编程,第323题,随机整数按位或运算

    用欧拉计划学Rust编程,第323题,随机整数按位或运算

    题目描述:

    理解题意,一个数初始值为0,不断与一个随机数进行‘或’运算,直到32个二进制位都为1,所需要步数记为N。进行无数数这样的试验,问N的平均值是多少。

    第一步:

    先用蒙特卡罗模拟方法试验一下,看看答案的大概范围。

    fn trial() -> u32 {
        let mut x = 0_u32;
        for i in 1.. {
            x |= rand::random::<u32>();
            if x == 0xFFFFFFFF {
                return i;
            }
        }
        0
    }
    

    运行这个trail()函数10万次,取平均值,可以得到答案在6.35附近。

    第二步:

    原题的x是32位整数,可以从最简单的情况入手,假设x为1位的整数。
    记此时的期望值为E(1)。
    进行第一步操作之后,有两种可能:
    第一种情况,1/2的概率,直接变成了1,即一步完成
    第二种情况,1/2的概率,仍是0,回到了初始情况,即需要1+E(1)步完成。
    这时有:E(1) = 1/2 + 1/2 * (1 + E(1))
    可以解出:E(1) = 2

    第三步:

    假设x是2位整数,此时的期望值记为E(2)。
    进行第一步操作之后,有三种可能:
    第一种情况,1/4的概率,直接变成了(11)binary,即一步完成
    第二种情况,2/4的概率,有一位变为1,即(01)binary或(10)binary,这时是E(1)时的情况,期望值为1+E(1)。
    第三种情况,1/4的概率,仍为0,这时是1+E(2)。
    列出式子,E(2) = 1/4 + 2/4 * (1+ E(1)) + 1/4 * (1 + E(2))
    3/4 * E(2) = 8/4
    E(2) = 8/3

    第四步:

    这时,你可能还没有总结出规律,再来一次,假设x是3位整数,此时的期望值记为E(3)。
    进行第一步操作之后,有四种可能:
    第一种情况,1/8的概率,直接变成了(111)binary,即一步完成
    第二种情况,3/8的概率,有两位变为1,即(011)binary或(101)binary或(110)binary,这时是E(1)时的情况,期望值为1+E(1)。
    第三种情况,3/8的概率,有一位变为1,即(001)binary或(010)binary或(100)binary,这时是E(2)时的情况,期望值为1+E(2)。
    第四种情况,仍为0,这时是1+E(3)。
    列出式子,E(3) = 1/8 + 3/8 * (1+ E(1)) + 3/8 * (1 + E(2)) + 1/8 * (1 + E(3))
    7/8 * E(3) = 1/8 + 3/8 * (1+ E(1)) + 3/8 * (1 + E(2)) + 1/8
    7/8 * E(3) = 1/8 + 3/8 * 3 + 3/8 * 11/3 + 1/8
    E(3) = 22/7

    第五步,现在可以总结出最一般的规律:

    E(N) = 1/2^N + C(N,1)/2^N * (1+E(1)) + C(N,2)/2^N * (1+E(2)) + ... + C(N,N-1)/2^N * (1+E(N-1)) + 1/2^N (1+E(N))
    这里的C(N, i)是排列组合数,从N个数里取i个数的组合数。
    耐心地把右侧的E(N)挪到左侧:
    (2^N-1)/2^N * E(N) = 1/2^N + C(N,1)/2^N * (1+E(1)) + C(N,2)/2^N * (1+E(2)) + ... + C(N,N-1)/2^N * (1+E(N-1)) + 1/2^N
    最后可以得到:
    E(N) = [1 + C(N,1) * (1+E(1)) + C(N,2) * (1+E(2)) + ... + C(N,N-1) * (1+E(N-1)) + 1] / (2^N-1)
    我们知道E(1)=2,不断套用上面的公式,就可以得到最终的答案。
    求组合数的函数:

    fn comb(n: usize, r: usize) -> f64 {
        let mut c = 1_f64;
        for i in 0..r {
            c *= ((n - i) as f64) / (r - i) as f64;
        }
        c
    }
    

    主程序:

    let mut e = vec![0.0, 2.0];
    for n in 2..=32 {
        let mut f = 1.0 as f64;
        for r in 1..=n {
            f += comb(n, r) * (1.0 + e[n - r]);
        }
        f = f / (2_f64.powf(n as f64) - 1.0);
        e.push(f);
        println!("E({}) = {:.10}", n, f);
    }
    
  • 相关阅读:
    嵌入式Linux c语言常用数据结构
    Project Euler 9
    串口应用开发的步骤
    创建简单的守护进程的编写规则
    Linux进程间通信信号通信
    [转]linux进程间的通信手段
    守护进程实例
    Linux slab 分配器剖析了解 Linux 内存管理的方式(转)
    嵌入式Linux c语言常用数据结构2
    嵌入式程序员应知道的几个题目(重要)
  • 原文地址:https://www.cnblogs.com/speeding/p/13629583.html
Copyright © 2011-2022 走看看