存储大量基数B的最佳方法是什么?

时间:2012-06-13 17:35:55

标签: c algorithm largenumber number-systems

存储大型基本B编号的最佳方法是什么,以便可以有效地完成右移和检查最低有效位等操作?

实际上,我遇到了一个采访问题

Given two numbers N and K such that 0<=N<=1000 and 0<=K<=1000^1000. I need 
to check that whether N^N = K or not. For example:

if N=2 and K=4 , 2^2 = 4 so return true;
if N=3 and K=26 , 3^3 != 26 so return false

我的想法是,如果我考虑base N number system,那么N^N将等同于1 followed by N zero。例如 - 对于N = 2,2 ^ 2 = 100(在基数2中),对于N = 3,3 ^ 3 = 1000(在基数3中)。然后我可以轻松编写一个函数来判断是否K = N^N

int check(unsigned long int N, unsigned long int K) 
{
    unsigned long int i;
    for (i=0; i<N; i++) 
    {
        if (K%N != 0) //checking whether LSB is 0 or not in base N number system 
           return 0;
        K = K/N; //right shift K by one. 
    }
    return (K==1);
}

现在这个功能有两个主要问题:

1) An unsigned long int is not sufficient to store large numbers of range 0 
to 1000^1000.
2) The modulus and the division operations makes it every less efficient.  

为了提高效率,我正在寻找一些方法来表示大的基数N,以便我可以执行右移并有效地检查最低有效位操作。以前有人遇到过这样的事吗?或者任何人都知道有效解决这个问题的其他方法吗?

5 个答案:

答案 0 :(得分:2)

要检查相等性,您实际上不必进行高精度算术 - 您可以使用http://en.wikipedia.org/wiki/Chinese_remainder_theorem。找到足够的质数以确保它们的乘积大于N ^ N,然后依次检查N ^ N对K的每个素数的模数。

实际上,我可能会使用Java BigInteger包来进行简单的计算。

答案 1 :(得分:2)

根据面试官的不同,可能会接受一些答案。如果其中任何一项都不被接受,那么希望面试官能够迅速推动您提出其他建议。

  • 使用gmp,然后使用mpz_t代替unsigned long进行分割和模式,或者只计算N^N并将其与K进行比较。这是最简单的方法。
  • 编写自己的大数字库,可能使用基数10作为内部表示而不是基数2,如果输入位于基数10中,并且认为坚持使用它可能比首先转换为二进制更快。当然,它不一定是一个完整的算术库,你真正需要的就是除法余数。
  • 发明一些你可以在开始之前做的快速测试,以避免在某些方面K远离N^N时很多工作。例如,测试log(K) / N是否大约等于log(N),并且在任何基数中采用的日志对于输入最方便。或者测试KN可以被210等方便的数字整除多少次:如果K不能完全整除N时间是N的倍数,那显然是错误的。或者测试它们是否等于ULONG_MAX1000000之类的小数。不幸的是,这种事情只能加速某些不平等的情况,它会减慢其他一切,包括它们的情况。所以它可能适得其反,取决于你期望的输入。
mcdowella的回答可能是也可能不是最好的,我不知道。特别有希望的是你只需要生成一次素数(当你编写程序时),从1009开始的1000个素数对于N <= 1000来说已经绰绰有余了。较大的素数意味着需要的更少,因此工作量更少,特别是如果它们不大于ULONG_MAX的平方根。通过平方或等效使用取幂来获得每个素数的N^N模数,并且K在输入的任何基础上一次做几个数字。

要真正闪存,对于每个预先选择的素数p,您可以编写(或让编译器为您编写)模数p操作,该操作比适用于任何素数的一般整数模运算快除数。也就是说,一个像样的编译器i % 1009可能比i % j更快,其中j的值在编译时是未知的,但在运行时结果是1009.但要注意,速度的差异可能无法证明(比如)通过函数指针调用它的成本。因此,利用它可能需要一些丑陋的重复代码。

答案 2 :(得分:0)

为什么要将数字转换为基数N?

你可以继续除以N.如果你在N之后得到1这样的除数那么它就是N ^ N.否则它不是。

您必须将K存储为字符串并执行分割操作。

divide(k,n):
  c = ''
  a = ''
  for i in k:
    c = c+i
    a = a+ str(int(c)/ int(n))
    c = str(int(c) % int(n))
 return a

这是在python中,你可以在C

中实现类似的东西

答案 3 :(得分:0)

  给定两个数N和K使得0 <= N <= 1000且0 <= K <= 1000 ^ 1000。我需要检查是否N ^ N = K.

很大程度上取决于提供给代码时这些数字的存储方式。假设它们是文本格式,我只是保持这种方式,并创建一个包含N ^ N值的1001个字符串数组。您可以使用像bc这样的任意精度算术命令行程序来一次性创建这些字符串,并在循环中调用它。

答案 4 :(得分:0)

由于1000 ^ 1000中只有1000个“好”值(并且它们将彼此相距很远),您可以先使用一些对数近似来猜测 N 。之后,您最多只需要一次精确检查(例如,使用一些bignum库)。

这个对数不一定非精确,即使 strlen()也足够近似。