Какова сумма цифр числа 2 ^ 1000?

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

Проблема заключается в следующем:

2 ^ 15 = 32768, а сумма цифр 3 + 2 + 7 + 6 + 8 = 26.

Какова сумма цифр числа 2 ^ 1000?

Я понимаю предпосылку и математику проблемы, но я начал практиковать C # только неделю назад, поэтому мое программирование в лучшем случае шаткое.

Я знаю, что int, long и double безнадежно неадекватны для точного хранения 300+ (базовых 10) цифр 2 ^ 1000, поэтому нужна какая-то стратегия. Моя стратегия заключалась в том, чтобы установить вычисление, которое получает цифры одну за другой, и надеяться, что компилятор сможет выяснить, как вычислить каждую цифру без какой-либо ошибки, такой как переполнение:

using System;
using System.IO;
using System.Windows.Forms;

namespace euler016
{
    class DigitSum
    {
        // sum all the (base 10) digits of 2^powerOfTwo
        [STAThread]
        static void Main(string[] args)
        {
            int powerOfTwo = 1000;
            int sum = 0;

            // iterate through each (base 10) digit of 2^powerOfTwo, from right to left
            for (int digit = 0; Math.Pow(10, digit) < Math.Pow(2, powerOfTwo); digit++)
            {
                // add next rightmost digit to sum
                sum += (int)((Math.Pow(2, powerOfTwo) / Math.Pow(10, digit) % 10));
            }
            // write output to console, and save solution to clipboard
            Console.Write("Power of two: {0} Sum of digits: {1}\n", powerOfTwo, sum);
            Clipboard.SetText(sum.ToString());
            Console.WriteLine("Answer copied to clipboard. Press any key to exit.");
            Console.ReadKey();
        }
    }
}

Кажется, он отлично работает для powerOfTwo ‹34. В моем калькуляторе закончились значащие цифры выше этого, поэтому я не мог проверить более высокие степени. Но, отслеживая программу, похоже, что переполнения не происходит: количество вычисляемых цифр постепенно увеличивается по мере увеличения powerOfTwo = 1000, а также (в среднем) сумма цифр увеличивается с увеличением powerOfTwo.

Для фактического расчета, который я должен выполнить, я получаю результат:

Степень двойки: 1000 Сумма цифр: 1189

Но 1189 - неправильный ответ. Что не так с моей программой? Я открыт для любой конструктивной критики.


person travisbartley    schedule 11.10.2013    source источник
comment
Посмотрите, может ли BigInteger помочь вам msdn.microsoft.com/en- us / library /   -  person luiges90    schedule 11.10.2013
comment
@ luiges90, ох, не знал об этом. Я дам ему попробовать.   -  person travisbartley    schedule 11.10.2013
comment
сумма, очевидно, равна 1 (если вы используете базу 2)   -  person Display Name    schedule 11.10.2013
comment
Недостаток вашей программы в том, что вы очень быстро сталкиваетесь с ошибками точности; двойные числа имеют точность примерно до 15 знаков после запятой, и вам нужно 300, чтобы получить правильный ответ. Используйте BigInteger, как говорили другие. Возможно, вы захотите прочитать мою текущую серию статей о том, как реализовать свою собственную большую целочисленную библиотеку; к началу следующей недели будет достаточно кода для решения вашей проблемы, хотя, поскольку алгоритмы рекурсивны, 2 ^ 1000 взорвет стек. Начните здесь: ericlippert.com/2013/09/16/ математика с нуля, часть первая   -  person Eric Lippert    schedule 11.10.2013
comment
Подумайте об этом так: очевидно, что для точного представления чисел на 2 ^ 1000 вам нужно порядка 1000 бит. У дубля 64.   -  person Eric Lippert    schedule 11.10.2013
comment
@EricLippert, спасибо, я посмотрю, я пришел к такому выводу после того, как понял, что Math.Pow - это не волшебная палочка, которая психически знает нужную мне точность.   -  person travisbartley    schedule 11.10.2013


Ответы (10)


Обычный int не может помочь вам с таким большим количеством. Даже long. Они никогда не предназначены для обработки таких огромных чисел. int может хранить около 10 цифр (точное максимальное значение: 2,147,483,647) и long около 19 цифр (точное максимальное значение: 9,223,372,036,854,775,807). Однако быстрый расчет с помощью встроенного калькулятора Windows показывает, что 2^1000 - это число, состоящее более чем из 300 цифр.

(примечание: точное значение можно получить из int.MAX_VALUE и long.MAX_VALUE соответственно)

Если вам нужна точная сумма цифр, даже float или double не будут работать, потому что они хранят только значащие цифры для нескольких или нескольких десятков цифр. (7 цифр для чисел с плавающей запятой, 15-16 цифр для чисел с плавающей запятой). Подробнее о представлении с плавающей запятой, двойной точности см. здесь.

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


Вернемся к вашему коду, я думаю, проблема здесь

Math.Pow(2, powerOfTwo)

Это выходит за рамки расчета. Ну, не совсем, но, как я уже сказал, точность double не совсем точно отражает фактическое значение результата.

person luiges90    schedule 11.10.2013
comment
Ха-ха! Да, я получил правильный ответ, используя BigInteger. Я понял ваш последний абзац, когда читал свой вопрос. Math.Pow дает двойное значение, поэтому оба члена в этом уравнении не совсем точно представляют то, для чего они предназначены. - person travisbartley; 11.10.2013
comment
На самом деле, я рекомендую вам изучить математику и постараться не использовать BigInteger :) к сожалению, я уже выбросил эту математику :( - person luiges90; 11.10.2013
comment
Вы правы, BigInteger почти обманывает. Но какое это мощное секретное оружие. Я думаю, что это может значительно упростить многие решения Project Euler, хотя может быть не оптимальным или образовательным. - person travisbartley; 11.10.2013
comment
@ trav1s просто не занимается программированием, но небольшая математика может помочь сэкономить ваши вычисления и, конечно же, так много циклов процессора - person dbw; 11.10.2013
comment
Возможно, это не самое эффективное решение, но я принимаю его, потому что это было то, что я искал: оно указывает на проблему с моей программой и показывает, как ее можно исправить. Он не предоставляет исходный код прямо. Наконец, получившаяся программа имеет очень низкое время выполнения, около 1 мс на моей скромной машине. Я уверен, что есть несколько отборных математических самородков, которые можно было бы извлечь, сделав это по-другому, но я доволен тем, что делаю это таким образом. - person travisbartley; 11.10.2013
comment
@ trav1s, мы здесь, чтобы помочь вам и предложить решение, которое наилучшим образом соответствует нашим знаниям и опыту, и также правильно, что вы принимаете ответ, который, по вашему мнению, решил вашу проблему (+1 за это). - person dbw; 11.10.2013
comment
2 ^ 10 ≈ 10 ^ 3, поэтому каждые 10 бит соответствуют примерно 3 десятичным цифрам или просто подсчитывают 3 бита для каждой цифры, что оставляет вам ~ 1000/3 десятичных цифр. - person phuclv; 25.09.2014

Чтобы вычислить значения таких больших чисел, вам нужно быть не только хорошим программистом, но и хорошим математиком. Вот вам подсказка, есть знакомая формула a x = e x ln a или, если хотите, a x = 10 x зарегистрировать.

Более конкретно для вашей проблемы 2 1000 Найдите общий (базовый 10) логарифм 2 и умножьте его на 1000; это степень 10. Если вы получите что-то вроде 10 53.142 (53.142 = log 2 value * 1000) - что вы, скорее всего, получите - тогда это 10 53 x 10 0,142; просто оцените 10 0,142, и вы получите число от 1 до 10; и умножьте это на 10 53, но эти 10 53 не будут полезны, так как сумма 53 с нулевой суммой будет равна только нулю.

Для расчета журнала в C #

Math.Log(num, base);

Для большей точности вы можете использовать Log и Pow функции Big Integer.

Теперь остальное помощь в программировании, я думаю, вы можете получить со своей стороны.

person dbw    schedule 11.10.2013
comment
Я пытался понять это, но пока не понимаю. Итак, 2 ^ 1000 = 10 ^ (1000 * log_10 (2)). Ладно, пока что с тобой. Тогда вы получите что-то вроде 10 ^ 53.142 как? Я получил 10 ^ 301.0299957 ... и я думаю, что десятичное число по-прежнему превышает тип double. - person travisbartley; 11.10.2013
comment
@ trav1s 53.142 - это просто пример, а не фактическое значение журнала 2 - person dbw; 11.10.2013
comment
@ trav1s есть функция журнала и мощности для BigInetgers, и я уверен, что вы можете быть точными только до этих значений - person dbw; 11.10.2013
comment
А затем округлите до ближайшего целого числа, я думаю. Это должно быть более эффективным с точки зрения памяти, чем одновременный расчет всего числа. Не уверен, что это действительно может сэкономить циклы ЦП, как вы утверждаете, хотя с таким количеством вычислений журнала ... - person travisbartley; 11.10.2013
comment
@ trav1s, почему нам нужно округлить до ближайшего целого числа, наконец, вам нужна сумма всех переменных, и вы можете перейти к каждому значению, преобразовав BigInt в строку, и да, циклы ЦП будут сохранены. - person dbw; 11.10.2013

Решение без использования класса BigInteger - хранить каждую цифру в ее собственном int, а затем выполнять умножение вручную.

static void Problem16()
{
    int[] digits = new int[350];

    //we're doing multiplication so start with a value of 1
    digits[0] = 1;
    //2^1000 so we'll be multiplying 1000 times
    for (int i = 0; i < 1000; i++)
    {
        //run down the entire array multiplying each digit by 2
        for (int j = digits.Length - 2; j >= 0; j--)
        {
            //multiply
            digits[j] *= 2;
            //carry
            digits[j + 1] += digits[j] / 10;
            //reduce
            digits[j] %= 10;
        }
    }

    //now just collect the result
    long result = 0;
    for (int i = 0; i < digits.Length; i++)
    {
        result += digits[i];
    }

    Console.WriteLine(result);
    Console.ReadKey();
}
person JoeClacks    schedule 29.01.2014

Я использовал побитовый сдвиг влево. Затем преобразование в массив и суммирование его элементов. Мой конечный результат - 1366. Не забудьте добавить ссылку на System.Numerics;

BigInteger i = 1;
         i = i << 1000;
        char[] myBigInt = i.ToString().ToCharArray();
        long sum = long.Parse(myBigInt[0].ToString());
        for (int a = 0; a < myBigInt.Length - 1; a++)
        {
            sum += long.Parse(myBigInt[a + 1].ToString());
        }
        Console.WriteLine(sum);
person ttitto    schedule 11.10.2013

поскольку вопрос специфичен для C #, использование bigInt может сработать. в java и python он тоже работает, но на таких языках, как c и c ++, где средство недоступно, вам нужно взять массив и выполнить умножение. возьмите большую цифру в массиве и умножьте ее на 2. Это будет просто и поможет улучшить ваши логические навыки. и пришел к проекту Эйлера. есть задача в которой надо найти 100! вы можете применить ту же логику и для этого.

person WannaBeCoder    schedule 31.12.2013

Попробуйте использовать BigInteger type, 2 ^ 100 будет очень большим числом для обработки даже double.

person John K Samuel    schedule 11.10.2013

Вот мое решение на JavaScript

(function (exponent) {
  const num = BigInt(Math.pow(2, exponent))
  let arr = num.toString().split('')
  arr.slice(arr.length - 1)
  const result = arr.reduce((r,c)=> parseInt(r)+parseInt(c))
  console.log(result)
})(1000)

person Soumyajit Mohapatra    schedule 02.03.2021

Это несерьезный ответ - просто наблюдение.

Хотя попытаться превзойти Project Euler, используя только один язык программирования, - хороший вызов, я считаю, что этот сайт призван расширить горизонты всех программистов, которые попытаются это сделать. Другими словами, рассмотрите возможность использования другого языка программирования.

для решения этой проблемы в Common Lisp может быть таким же простым, как

(defun sum_digits (x)
    (if (= x 0)
        0
        (+ (mod x 10) (sum_digits (truncate (/ x 10))))))

(print (sum_digits (expt 2 1000)))
person wjm    schedule 11.10.2013

Python позволяет очень просто вычислить это с помощью одной строки:

print sum(int(digit) for digit in str(2**1000))

или, альтернативно, с картой:

print sum(map(int,str(2**1000)))
person Stefan Gruenwald    schedule 06.11.2014

person    schedule
comment
Если вы добавите описание к своему ответу, это очень поможет - person Uriil; 21.06.2014