【问题标题】:decimal value of the number formed by concatenating the binary representations of first n natural numbers通过连接前 n 个自然数的二进制表示形成的数字的十进制值
【发布时间】:2020-10-19 05:53:12
【问题描述】:

给定一个数字n,找出由前n个自然数的二进制表示连接而成的数字的十进制值。
打印答案模 10^9+7。

另外,n 可以大到 10^9,因此需要对数时间方法。

例如:n=4,答案 = 220

解释:数字形成=11011100 (1=1,2=10,3=11,4=100)。 11011100="220" 的十进制值。

我在下面使用的代码仅适用于第一个整数 N

    String input = "";
    for(int i = 1;i<=n;i++) {
        input += (Integer.toBinaryString(i));
    }
    return Integer.parseInt(input,2);

【问题讨论】:

  • 您的值太大,无法存储在Integer 中。尝试更大的对象类,例如Long。如果你想要无限规模,请使用BigInteger
  • 当改为Long时,一直工作到18个整数
  • return new BigInteger(input, 2);

标签: java math binary mod


【解决方案1】:

This solution 这个问题需要O(N) 时间。幸运的是,这可以在O(logN) 时间内解决。另外,这是A047778 序列:

1,6,27,220,1765,14126,113015,1808248,28931977, 462911642,7406586283,118505380540,1896086088653, 30337377418462,485398038695407,15532737238253040, 497047591624097297,15905522931971113522

序列遵循这种递归关系: 其中⌊.⌋是floor function

a(n)也可以表示为多个arithmetico–geometric series之和。

如果我们对a(14) 感兴趣,这里是它的计算方法。

在上述等式两边同时乘以 2 的幂得到如下等式:

如果我们将上述所有方程相加,a(14) 可以表示为four 算术-几何级数之和。

需要注意的是,在除第一个序列之外的所有序列中,等差数列的第一项的形式为,最后一项的形式为

算术几何序列的 n 项之和可以使用此formula 计算:

a(First term of AP), n(Number of terms), d(Common Difference of AP), b(First term of GP), r(Common ratio of GP). 

由于我们感兴趣的是 a(n) mod 1000000007 而不是实际的术语 a(n),因此这些模运算可能会派上用场。

This 是实现除法模数的良好起点,这需要一些数论基础知识。

一旦我们计算出所需的序列数和每个序列的五个变量a, n, d, b, r,就可以在O(logn) 时间内计算出a(n) modulo 1000000007

这是一个有效的C++ 代码:

#include <numeric>
#include <iostream>
#define mod long(1e9+7)

long multiply(long a,long b){
    a%= mod;b%= mod;
    return (a*b)%mod;
}

void inverseModulo(long a,long m,long *x,long *y){ //ax congruent to 1 mod m

    if(!a){
        *x=0;
        *y=1;
        return ;
    }
    long x1,y1;
    inverseModulo(m%a,a,&x1,&y1);
    *x=y1-(m/a)*x1;
    *y=x1;
    return;
}

long moduloDivision(long a,long b,long m){  // (a*(returnValue))mod m congruent to b mod m
    //https://www.geeksforgeeks.org/modular-division/ and https://www.geeksforgeeks.org/multiplicative-inverse-under-modulo-m/
    long x,y;
    inverseModulo(b, m, &x, &y);
    x%=m;
    return (x*a)%m;
}

long power(long n,long r){ //calculates (n^r)%mod in logarithmic time
    if(r==0) return 1;
    if(r==1) return n;
    if(r%2){
        auto tmp=power(n, (r-1)/2);
        return multiply(multiply(n,tmp),tmp);
    }
    auto tmp=power(n, r/2);
    return multiply(tmp, tmp);
}
long sumOfAGPSeries(long a,long d,long b,long r,long n){
    if(r==1) return multiply(n, multiply(a, 2)+multiply(n-1, d))/2;
    long left=multiply(multiply(d, r), power(r,n-1)-1);
    left=a+moduloDivision(left,r-1,mod);
    left=multiply(left, b);
    left%=mod;
    long right=multiply(multiply(b, power(r, n)), a+multiply(n-1, d));
    long ans=right-left;
    ans=(ans%mod + mod) % mod;
    return moduloDivision(ans,r-1,mod);

}
signed main(){
    long N=1000;
    long ans = 0;
    long bitCountOfN = log2(N) + 1;
    long nearestPowerOfTwo = pow(2, bitCountOfN - 1);
    long startOfGP = 0;
    while (nearestPowerOfTwo) { // iterating over each arithmetico–geometric sequence
        long a, d, b, r, n;
        a = N;
        d = -1;
        b = power(2, startOfGP);
        r = power(2, bitCountOfN);
        n = N - nearestPowerOfTwo + 1;
        ans += sumOfAGPSeries(a, d, b, r, n);
        ans %= mod;
        startOfGP += n * bitCountOfN;
        N = nearestPowerOfTwo - 1;
        nearestPowerOfTwo >>= 1;
        bitCountOfN--;
    }
    std::cout << ans << std::endl;
    return 0;
}

上述C++代码的有效性可以使用这个简单的python代码来验证:

def a(n): 
  return int("".join([(bin(i))[2:] for i in range(1, n+1)]), 2)
for n in range(1,100):
  print (a(n)%1000000007)

【讨论】:

    【解决方案2】:

    请注意,不必使用字符串表示形式(此外,在任务更改后没有用处)。看按位算术的方法(Python,但原理是一样的)

    有了关于模 1000000007 的新条件,我们只需在每一步的结果计算行中添加模运算,因为左移和 or-ing 相当于乘以 2 的幂再加,这些运算服从模的等价关系特性。注意中间结果不超过1000000007*n,所以long类型在这里适合合理的n值。

    n = 100  
    size = 0   #bit length of addends
    result = 0   # long accumulator
    for i in range(1, n + 1):    
        if i & (i - 1) == 0:    #for powers of two we increase bit length
            size += 1
        result = ((result << size) | i) % 1000000007   #shift accumulator left and fill low bits with new addend
    print(result)
    

    没有按位运算的变体:

    pow2 = 1
    nextpow = 2
    result = 0   # long accumulator
    for i in range(1, n + 1):
        if i == nextpow:    #for powers of two we increase bit length
            pow2 = nextpow
            nextpow = nextpow * 2
        result = (result * pow2  + i) % 1000000007  #shift accumulator left and fill low bits with new addend
    

    【讨论】:

    • 因为串联很大,我们可以使用模10^9 + 7
    • 这种情况彻底改变了问题。值得编辑问题。
    • 是的,我编辑了答案,检查了结果是否与长算术一致。
    • @MBo 由于约束非常大,这种 O(N) 方法不可扩展。存在 O(logN) 方法
    • @duplex143 但是作者没有提到n约束,所以我不明白DV原因
    【解决方案3】:
        cin>>n;
        ll ans=1;
        ll one=1;
        for(int i=2;i<=n;i++)
        {
            ll digit=log2(i)+1;
            ans=(((ans%N*(one<<digit)%N)%N+i%N)%N);
        }
        cout<<ans<<Ed;
    

    【讨论】:

    • 请不要只发布代码作为答案,还要解释您的代码的作用以及它如何解决问题的问题。带有解释的答案通常更有帮助,质量更高,更有可能吸引投票。
    猜你喜欢
    • 2019-07-29
    • 2018-03-20
    • 2018-02-25
    • 1970-01-01
    • 2016-01-01
    • 1970-01-01
    • 2016-01-10
    • 2011-04-17
    • 2011-12-27
    相关资源
    最近更新 更多