Как умножить два больших числа - PullRequest
11 голосов
/ 18 июля 2010

Вам дан список из n чисел L=<a_1, a_2,...a_n>. Каждый из них либо 0, либо в форме +/- 2 k , 0 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 ]

6 голосов
/ 18 июля 2010

В своем первом ответе я рассмотрел проблему ОП в «умножении двух больших чисел».Оказывается, это желание - лишь малая часть гораздо более серьезной проблемы, которую я собираюсь рассмотреть сейчас:

"Я до сих пор не пришел к окончательному скелету моего алгоритма.Интересно, не могли бы вы помочь мне с этим? "

(см. вопрос для описания проблемы)

Все, что я собираюсь сделать, это объяснить подход, предложенный Амноном чуть более подробно, так что вся заслуга должна идти к нему.

Вы должны найти самый большой продукт непрерывного подсписка из списка целых чисел, которые имеют степени 2. Идея состоит в том, чтобы:

  1. Вычисляет произведение каждого непрерывного подсписка.
  2. Возвращает самый большой из всех этих продуктов.

Вы можете представитьподсписок по его start и end индексам.Для start=0 существует n-1 возможных значений для end, а именно 0..n-1.При этом создаются все подсписки, которые начинаются с индекса 0. На следующей итерации вы увеличиваете start на 1 и повторяете процесс (на этот раз существует n-2 возможных значений для end).Таким образом, вы генерируете все возможные подсписки.

Теперь для каждого из этих подсписков вы должны вычислить произведение его элементов - то есть придумать метод 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]);       
}
4 голосов
/ 18 июля 2010

На самом деле, самый эффективный способ умножения - это делать сложение.В этом особом случае все, что у вас есть, это числа, которые являются степенями двух, и вы можете получить произведение подсписка, просто сложив экспоненты вместе (и подсчитав отрицательные числа в вашем продукте и сделав его отрицательным числом в случае нечетногонегативы).

Конечно, для сохранения результата вам может понадобиться BigInteger, если у вас закончились биты.Или, в зависимости от того, как должен выглядеть вывод, просто скажите (+/-) 2 ^ N, где N - сумма показателей степени.

Синтаксический анализ входных данных может быть вопросом случая переключения, так как вытолько 30 номеров, чтобы заботиться.Плюс негативы.

Это скучная часть.Интересно то, как вы получаете подсписок, который производит наибольшее число.Вы можете принять тупой подход, проверив каждый вариант, но в худшем случае это будет алгоритм O (N ^ 2) (IIRC).Что на самом деле не очень хорошо для длинных входов.

Что вы можете сделать?Вероятно, я бы начал с самого большого неотрицательного числа в списке как подсписка и увеличил бы подсписок, чтобы получить как можно больше неотрицательных чисел в каждом направлении.Затем, имея в наличии все положительные стороны, перейдите к парам отрицательных с обеих сторон, например.только расти, если вы можете расти по обе стороны списка.Если вы не можете расти в обоих направлениях, попробуйте одно направление с двумя (четырьмя, шестью и т. Д. Четными) последовательными отрицательными числами.Если вы не можете расти даже таким образом, остановитесь.

Ну, я не знаю, работает ли этот алогритм, но если он (или что-то подобное) работает, это алгоритм O (N), что означаетотличное выступление.Давайте попробуем!: -)

4 голосов
/ 18 июля 2010

Поскольку k <= 30, любое целое число i = 2 <sup>k поместится в Java int.Однако произведение таких двух целых чисел может не обязательно соответствовать Java int, поскольку 2 k * 2 k = 2 2 * k <= 2 <sup>60 , которые заполняют в Java long.Это должно ответить на Ваш вопрос относительно "(умножения) двух чисел ...".

В случае, если Вы захотите умножить более двух чисел, что подразумевается Вашим назначением, говоря "... наибольшийпроизведение НЕПРЕРЫВНОГО СУБЛИСТА ... "(длина подсписка может быть> 2), взгляните на класс Java <a href="http://download.oracle.com/docs/cd/E17476_01/javase/1.4.2/docs/api/java/math/BigInteger.html" rel="nofollow noreferrer">BigInteger</a>.

3 голосов
/ 18 июля 2010

Хммм ... поскольку все они имеют степень 2, вы можете просто добавить показатель степени вместо умножения чисел (эквивалентно логарифму произведения). Например, 2 ^ 3 * 2 ^ 7 равно 2 ^ (7 + 3) = 2 ^ 10. Я оставлю обработку знака в качестве упражнения для читателя.

Что касается проблемы с подсписками, то существует менее n ^ 2 пар индексов (начало, конец). Вы можете проверить их все или попробовать решение динамического программирования .

3 голосов
/ 18 июля 2010

РЕДАКТИРОВАТЬ: я скорректировал схему алгоритма, чтобы она соответствовала фактическому псевдокоду, и поместил анализ сложности непосредственно в ответ:

Схема алгоритма

Последовательно пройдем попоследовательность и значение магазина и первый / последний индекс продукта (положительный) с момента последнего 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) или линейной.

1 голос
/ 19 июля 2010

Смотрите этот код. Здесь я реализую точный факториал огромного количества. Я просто использую целочисленный массив, чтобы делать большие числа. Загрузите код с Исходный код планеты .

1 голос
/ 18 июля 2010

Я хотел бы объединить наблюдение Амнона о умножении степеней 2 с одним из моих в отношении подсписков.

Списки заканчиваются на 0. Мы можем разбить проблему на нахождение самого большого продукта в каждом подсписке, а затем максимум этого. (Другие упоминали об этом).

Это моя 3-я редакция этой рецензии. Но 3 очарование ...

подход

Учитывая список ненулевых чисел, (это то, что потребовалось много времени), есть 3 подслучаа:

  1. Список содержит четное количество отрицательных чисел (возможно, 0). Это тривиальный случай, оптимальный результат - произведение всех чисел, гарантированно положительных.
  2. Список содержит нечетное число отрицательных чисел, поэтому произведение всех чисел будет отрицательным. Чтобы изменить знак, необходимо пожертвовать подпоследовательностью, содержащей отрицательное число. Два подслоя:

    а. приносить в жертву числа слева направо, включая самый левый минус; или

    б. жертвовать числами от правых до самых правых отрицательных включительно.

    В любом случае верните произведение оставшихся чисел. Пожертвовав ровно одним отрицательным числом, результат, несомненно, будет положительным. Выберите победителя (а) и (б).

Осуществление

Входные данные должны быть разделены на подпоследовательности, ограниченные 0. Список может быть обработан на месте, если для его создания используется метод драйвера, который выбирает начало и конец не-0 последовательностей.

Выполнение математики в длинных только удвоит возможный диапазон. Преобразование в log2 упрощает арифметику с большими продуктами. Это предотвращает сбой программы на больших последовательностях больших чисел. В качестве альтернативы было бы возможно сделать всю математику в Bignums, но это, вероятно, работало бы плохо.

Наконец, конечный результат, все еще число log2, должен быть преобразован в печатную форму. Bignum пригодится там. Есть new BigInteger("2").pow(log);, который поднимет 2 до степени log.

Сложность

Этот алгоритм работает последовательно через подсписки, обрабатывая каждый из них только один раз. В каждом подсписке есть надоедливая работа по преобразованию входных данных в log2 и обратно, но усилия линейны по размеру списка. В худшем случае, большая часть списка вычисляется дважды, но это также линейная сложность.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...