如何乘以两个大数字

时间:2010-07-18 14:54:52

标签: java arrays multiplication

您将获得n个数字L=<a_1, a_2,...a_n>的列表。他们每个人都是 0或者+/- 2 k 形式,0 <= k <= 30。描述并实现 返回CONTINUOUS SUBLIST的最大乘积的算法 p=a_i*a_i+1*...*a_j, 1 <= i <= j <= n

例如,对于输入<8 0 -4 -2 0 1>,它应该返回8(8 或(-4)*( - 2))。

您可以使用任何标准编程语言,并可以假设 该列表以任何标准数据结构给出,例如int[]vector<int>List<Integer>

算法的计算复杂度是多少?

7 个答案:

答案 0 :(得分:6)

在我的第一个回答中,我解决了OP“两个大数字倍增”的问题。事实证明,这个愿望只是我要解决的一个更大问题的一小部分:

  

“我还没有到达算法的最终骨架,我想知道你是否可以帮我解决这个问题。”

(参见问题描述的问题)

我要做的就是更详细地解释the approach Amnon proposed,所以所有的功劳都归功于他。

你必须从2的幂的整数列表中找到连续子列表的最大乘积。想法是:

  1. 计算每个连续子列表的产品。
  2. 返回所有这些产品中最大的一个。
  3. 您可以通过其startend索引表示子列表。对于start=0end有n-1个可能的值,即0..n-1。这将生成从索引0开始的所有子列表。在下一次迭代中,您将start递增1并重复该过程(这次,end有n-2个可能的值)。这样您就可以生成所有可能的子列表。

    现在,对于每个子列表,您必须计算其元素的乘积 - 这是一个方法computeProduct(List wholeList, int startIndex, int endIndex)。您可以使用内置的BigInteger类(应该能够处理您的任务提供的输入)来避免进一步的麻烦,或者尝试实现其他人所描述的更有效的乘法方式。 (我将从更简单的方法开始,因为更容易看出你的算法是否正常工作,然后首先尝试优化它。)

    现在您可以迭代所有子列表并计算其元素的乘积,确定具有最大乘积的子列表应该是最简单的部分。

    如果您仍然难以在两个步骤之间建立联系,请告诉我们 - 但是,当您处理问题时,请同时向我们提供您的代码草稿,以便我们不会最终逐步构建解决方案和你复制和粘贴它。

    编辑:算法骨架

    public BigInteger listingSublist(BigInteger[] biArray)
    {       
        int start = 0;
        int end = biArray.length-1;
        BigInteger maximum;
    
        for (int i = start; i <= end; i++)
        {
            for (int j = i; j <= end; j++)
            {
                //insert logic to determine the maximum product.
                computeProduct(biArray, i, j);
            }
        }
    
        return maximum;                
    }
    
    public BigInteger computeProduct(BigInteger[] wholeList, int startIndex, 
                                                             int endIndex)
    {
        //insert logic here to return
        //wholeList[startIndex].multiply(wholeList[startIndex+1]).mul...(
        //    wholeList[endIndex]);       
    }
    

答案 1 :(得分:4)

由于k <= 30,任何整数i = 2 k 都适合Java int。然而,这两个整数的乘积可能不一定适合Java int,因为2 k * 2 k = 2 2 * k &lt; = 2 60 ,填入Java long。这应该回答你关于“(乘以两个数字......)的问题。”

如果你想要乘以两个以上的数字,这是你的作业所说的“......连续子列表的最大产品......”(一个子列表的长度可能> 2),看看Java的BigInteger类。

答案 2 :(得分:4)

实际上,最有效的乘法方式是做加法。在这种特殊情况下,你拥有的是2的幂数,你可以通过简单地将指数加在一起来获得子列表的乘积(并计算产品中的负数,并在奇数的情况下将其作为负数)底片)。

当然,要存储结果,如果用完了比特,可能需要BigInteger。或者根据输出的外观,只需说(+/-)2 ^ N,其中N是指数的总和。

解析输入可能是一个切换案例的问题,因为你只需要30个数字来处理。再加上否定因素。

这是无聊的部分。有趣的是如何获得产生最大数量的子列表。你可以通过检查每个变化来采取愚蠢的方法,但在最坏的情况下(IIRC),这将是一个O(N ^ 2)算法。这对于更长时间的输入来说真的不太好。

你能做什么?我可能从列表中最大的非负数开始作为子列表,并增加子列表以尽可能多地在每个方向上获得非负数。然后,在达到所有正面的情况下,在两边进行负对,例如。只有你能在名单的两边都成长,才会成长。如果你无法在两个方向上成长,请尝试使用两个(四个,六个,甚至是偶数)连续负数的一个方向。如果你不能以这种方式成长,请停止。

好吧,我不知道这个算法是否有效,但是如果它(或类似的东西)做了,它的O(N)算法,这意味着很好的性能。让我们试一试! : - )

答案 3 :(得分:3)

编辑:我调整了算法大纲以匹配实际的伪代码,并将复杂性分析直接放入答案:

算法概要

在序列和存储值以及产品的第一个/最后一个索引(正数)之后的顺序执行自上一个0.对另一个产品(负数)执行相同操作,该产品仅包含自序列的第一个符号更改以来的数字。如果你点击负序列元素交换两个产品(正面和负面)以及相关的起始指数。每当正产品达到新的最大存储量时,以及相关的开始和结束指数。在遍历整个序列之后,结果存储在最大变量中。

避免以二进制对数和另外的符号计算溢出。

伪码

maxProduct = 0
maxProductStartIndex = -1
maxProductEndIndex = -1
sequence.push_front( 0 ) // reuses variable intitialization of the case n == 0

for every index of sequence
   n = sequence[index]
   if n == 0
       posProduct = 0
       negProduct = 0
       posProductStartIndex = index+1
       negProductStartIndex = -1
   else
       if n < 0
           swap( posProduct, negProduct )
           swap( posProductStartIndex, negProductStartIndex )
           if -1 == posProductStartIndex // start second sequence on sign change
               posProductStartIndex = index
           end if
           n = -n;
       end if
       logN = log2(n) // as indicated all arithmetic is done on the logarithms
       posProduct += logN
       if -1 < negProductStartIndex // start the second product as soon as the sign changes first
          negProduct += logN
       end if
       if maxProduct < posProduct // update current best solution
          maxProduct = posProduct
          maxProductStartIndex = posProductStartIndex
          maxProductEndIndex = index
       end if
   end if
end for

// output solution

print "The maximum product is " 2^maxProduct "."
print "It is reached by multiplying the numbers from sequence index " 
print maxProductStartIndex " to sequence index " maxProductEndIndex

<强>复杂性

该算法在序列上使用单个循环,因此其O(n)乘以循环体的复杂性。身体最复杂的操作是log2。因此它的O(n)倍于log2的复杂性。多个有界大小的log2是O(1),因此产生的复杂度是O(n)又称线性。

答案 4 :(得分:3)

嗯..因为它们都是2的幂,你可以添加指数而不是乘以数字(相当于取产品的对数)。例如,2 ^ 3 * 2 ^ 7是2 ^(7 + 3)= 2 ^ 10。 我会把这个标志作为练习留给读者。

关于子列表问题,少于n ^ 2对(开始,结束)索引。您可以全部检查,或尝试dynamic programming解决方案。

答案 5 :(得分:1)

我想将Amnon关于2的倍增权的观察与我的一个关于子列表的观察结合起来。

列表以0结尾。我们可以将问题分解为找到每个子列表中最大的产品,然后是最大的产品。 (其他人已提到这一点)。

这是我对本文的第3次修订。但是3的魅力......

<强>方法

给出一个非0数字的列表(这需要经过深思熟虑),有3个子案例:

  1. 该列表包含偶数个负数(可能为0)。这是一个微不足道的案例,最佳结果是所有数字的乘积,保证是正数。
  2. 该列表包含奇数个负数,因此所有数字的乘积都为负数。要改变符号,有必要牺牲包含负数的子序列。两个子案例:

    一个。牺牲从左到右的数字,包括最左边的负数;或

    湾牺牲从右到右的数字,包括最右边的负数。

    在任何一种情况下,都要返回剩余数字的乘积。在牺牲了一个负数之后,结果肯定是正面的。选择(a)和(b)的获胜者。

  3. <强>实施

    需要将输入拆分为由0分隔的子序列。如果构建了一个驱动程序方法来遍历它并选择非0序列的开头和结尾,则可以在适当的位置处理该列表。

    以long为单位进行数学计算只会使可能的范围加倍。转换为log2可以更轻松地使用大型产品进行算术运算。它可以防止大数字序列上的程序失败。或者可以在Bignums中进行所有数学计算,但这可能表现不佳。

    最后,最终结果仍然是log2号,需要转换为可打印的形式。 Bignum在那里派上用场。有new BigInteger("2").pow(log);会使log的力量增加2。

    <强>复杂性

    此算法按顺序通过子列表工作,仅处理每一个。在每个子列表中,将输入转换为log2和返回结果是令人烦恼的工作,但是工作量与列表的大小呈线性关系。在最坏的情况下,列表的大部分总和被计算两次,但这也是线性复杂度。

答案 6 :(得分:1)

请参阅此代码。在这里,我实现了一个巨大的数字的精确因子。我只是使用整数数组来制作大数字。从Planet Source Code下载代码。