乘法和比较大数字

Posted

技术标签:

【中文标题】乘法和比较大数字【英文标题】:Multiplying and comparing big numbers 【发布时间】:2015-06-22 19:38:02 【问题描述】:

我有这个问题: 有 K 行 N 个数字(32 位)。我必须选择具有最大数字乘积的行。

主要问题是 N 可以达到 20。 我正在尝试用对数来做到这一点:

ld sum = 0, max = 0;
int index = 0;

for(int i = 0; i < k; i ++)  // K lines
    sum = 0, c = 0;
    for(int j = 0; j < n; j ++)  // N numbers
        cin >> t;
        if(t < 0)
            c++; // If the number is less than 0 i memorize it

        if(t == 1 || t == -1)  // if numbers = 1 OR -1
            sum += 0.00000001; // Because log(1) = 0
            if(t == -1)
                c ++;
        
        else if(t == 0)  // if some number is equal to zero then the sum is = 0
            sum = 0;
            break;
        
        else 
            sum += log10(fabs(t));
        
    

    if(c % 2 == 1) // if c is odd than multiply by -1
        sum *= -1;

    if(sum >= max)  
        max = sum;
        index = i;
    
    if((sum - max) < eps)  // if sum is equal to max i'm also have to choose it
        max = sum;
        index = i;
    

cout << index + 1 << endl;

该程序适用于 50% 的测试用例。有没有办法优化我的代码?

【问题讨论】:

您的程序只运行了一半,您想优化它吗?先让它工作...... @Borgleader 对不起我的英语,我只是找不到合适的词。我只想说也许计算有任何错误,或者你可以分享我可以找到信息的链接,谢谢! 那么 long double 的精度(我认为这是 ld 的 typedef'd 吗?)不足以代表您的产品。您应该使用任意精度的算术,例如 GMP 库或 Java 的 BigInteger 类提供的。 您是否尝试过将浮点值相乘?您将在现代台式 PC 上遇到的那种环境提供了范围远远超过 2^620 的双精度算术。当然,要获得精确的答案,您仍然需要多精度整数运算。 (如果这是一场比赛,请说明并提供链接。)(请说明numbers (32-bit) 是什么 - 假设自然,如果允许负整数,请说明什么是抢手的:最高有符号绝对乘积。)尝试除草 - 剔除因子1,产品至少包含一个因子0。考虑素数分解:计算每个素数和乘积。如果产品 a 的计数没有大于 b 并且至少小于一个,则丢弃产品 b。减去每个素数的最小计数。相乘和比较。 【参考方案1】:

在 t == -1 的情况下,您将 c 递增两次。

【讨论】:

【参考方案2】:

如果你想避免使用 bignum 库,你可以利用它,如果你将 b1b2 位数字相乘,那么结果就是 b1+b2 位长

    所以只需将一行中所有乘数的位数相加

    并进行比较

    记住某个数组中的结果

    int bits(DWORD p) // count how many bits is p DWORD is 32bit unsigned int
        
        DWORD m=0x80000000; int b=32;
        for (;m;m>>=1,b--)
         if (p>=m) break;
        return b;
         
    

    index 按结果位计数降序对行进行排序

    如果排序后的第一位也是最大值,那么它的行就是答案 如果您有不止一个最大值(更多行具有相同的位数并且也是最大值) 只有这样你才需要将它们相乘

现在是乘法

您知道应该一次将所有最大行数相乘 每次所有子结果都可以被同一个素数整除 除以它 这样,结果将被截断为更少的位数 所以它应该适合 64 位值 你应该检查素数直到 sqrt(最大值) 当您的最大值为 32 位时,请检查最高 65536 的素数 因此您可以制作一个静态素数表来检查以加快速度 也没有必要检查大于实际子结果的素数 如果您知道如何使用 Eratosthenes 的筛子极大地加速这一过程 但您需要在每次划分后跟踪索引偏移量并使用周期筛表,这有点复杂但可行 如果您不检查所有素数,而只检查少数选定的素数 那么结果还是会溢出 所以你也应该处理它(抛出一些错误或其他东西) 或将所有子结果除以某个值,但这会使结果无效

另一种乘法方法

您还可以按值对乘数进行排序 并检查是否有一些存在于所有最大行中 如果是,则将它们更改为一个(或从列表中删除) 这可以与之前的方法结合使用

大数乘法

您可以进行自己的 bignum 乘法 结果最大为 20*32=640 位 所以结果将是无符号整数数组(位宽 8,16,32 ...随你喜欢) 您也可以将数字作为字符串处理 在这里查看如何计算fast exact bignum square in C++ 它还包含乘法方法 这里是NTT based Schönhage-Strassen multiplication in C++ 但是对于像你这样的小数字来说,这会比较慢 最后你需要比较结果 因此,从 MSW 与 LSW 进行比较,哪条线中的数字更大就是最大线 (MSW 是最高有效字,LSW 是最低有效字)

【讨论】:

@Rasul btw bits(x)=ceil(log2(x))=ceil(log10(x)/log10(2))=ceil(ln(x)/ln(2)) 所以它类似于您的方法,但仅适用于整数算术【参考方案3】:

我认为这行肯定是错误的:

if(c % 2 == 1) // if c is odd than multiply by -1
    sum *= -1;

如果您的产品在 [0,1] 范围内,则其对数将为负数,这将使其为正数。我认为你应该把它分开。

【讨论】:

以上是关于乘法和比较大数字的主要内容,如果未能解决你的问题,请参考以下文章

仅当至少一个数字为 2 位时才能使用大数字(字符串)函数进行乘法运算

c_cpp 给定两个表示为字符串的数字,将数字作为字符串返回乘法。注意:数字可以任意大

大数据阶乘——字符串乘法器

如何使用递归函数(C#,D&C)避免非常大的数字的溢出乘法

剑指offer第12题打印从1到n位数以及大整数加法乘法

大整数乘法运算