Самый быстрый метод сложения/суммирования отдельных цифр числа

Некоторое время назад я видел вопрос на математическом форуме, где человек обсуждал сложение цифр в числе снова и снова, пока не будет получена одна цифра. (т.е. "362" станет "3+6+2", что станет "11"... тогда "11" станет "1+1", станет "2", поэтому "362" вернет 2... Я написал хороший код, чтобы получить ответ на этот вопрос, и разместил его только для того, чтобы его превзошел пользователь, который предположил, что любое число по модулю 9 равно этой «бесконечной сумме цифр», я проверил это, и он был прав... ну почти правильно, если возвращался ноль, вам приходилось переключать его на «9», но это было очень быстрое решение...

362 = 3+6+2 = 11 = 1+1 = 2

or...

362%9 = 2

В любом случае, метод mod9 работает фантастически для бесконечного сложения суммы цифр, пока не останется только одна цифра... но как насчет того, чтобы сделать это только один раз (т.е. 362 просто вернет "11")... Кто-нибудь может подумать быстрых алгоритмов?


person Albert Renshaw    schedule 19.02.2013    source источник
comment
Из любопытства. Было ли дано объяснение, почему %9 работает?   -  person Bernhard Barker    schedule 19.02.2013
comment
@Dukeling Нет, я предполагаю, что это как-то связано с тем фактом, что мы находимся в системе счисления с основанием 10, и это самое высокое однозначное число до сброса place (например, разряд десятков, разряд сотен и т. д.) ... Я бы предположил, что в системе счисления с основанием 6 это будет работать с модулем 5.   -  person Albert Renshaw    schedule 19.02.2013
comment
Вероятно, простая итерация - продолжайте брать частное и остаток, делящийся на десять, суммируя остатки и принимая частное в качестве значения для проверки на следующей итерации, и останавливайтесь, когда частное равно нулю.   -  person Steve314    schedule 19.02.2013
comment
Деление на 10 и суммирование остатка - это то, чем я сейчас занимаюсь, хотя мне интересно, знает ли кто-нибудь классные математические приемы для этого :о)   -  person Albert Renshaw    schedule 19.02.2013
comment
@Dukeling: 10^k mod 9 == 1 для любого целого числа k. Таким образом, для целых чисел d1, d2,... и e1, e2,... имеем d1*10^e1 + d2*10^e2 +... mod 9 = d1*10^e1 mod 9 + d2*10. *e2 mod 9 ... = d1*1 mod 9 + d2*1 mod 9 + ... = d1 + d2 + ... mod 9. Здесь d1... — цифры числа, а e1. .. являются показателями числа 10, соответствующими каждой позиции цифры. В 10 и 9 нет ничего особенного; он будет работать для любого основания b и соответствующего модуля b-1.   -  person rici    schedule 19.02.2013
comment
@AlbertRenshaw: Вы правы в своем предположении. Точнее, число N по основанию b (b > 2) равно просто Sum[exp=0..inf](digit*b^exp), мы знаем b^exp mod (b - 1) = 1, следовательно, N mod (b - 1) = Sum[exp=0..inf](digit) mod (b - 1).   -  person nhahtdh    schedule 19.02.2013
comment
@Dukeling Модуль 9 работает с относительно небольшими числами ... например, 8 сумм к 8, 9 сумм к 9, 10 сумм к 1, 11 сумм к 2, так что вы можете увидеть закономерность, что сумма просто продолжает цикл 1-9, 1-9, 1-9... но с более высокими числами, вот когда я запутался, почему это работает... когда есть больше итераций, например 365 = 11 = 2.   -  person Albert Renshaw    schedule 19.02.2013
comment
Нет, дело не в шаблонах. Берите пример и смотрите. Теперь 365 = 3 * 10 ^ 2 + 6 * 10 ^ 1 + 5 * 10 ^ 0 ... теперь, как упомянул @Dukeling, 10 ^ y mod 9 = 1. Таким образом, даже если вы умножите эту 1 на любое другое число, вы все равно получите этот номер. то есть 365 = 300 + 60 + 5, и вы уже знаете, какой модуль каждого возвращает. То же самое применимо к любому числу N в базовой системе b. Это также объясняет, почему, когда вы получаете ноль, вам нужно заменить его на 9 (9 по модулю 9 = 0).   -  person Ricketyship    schedule 19.02.2013
comment
Этот repeated digital sum также называется digital root. Трюк по модулю 9 — это всего лишь формула конгруэнтности.   -  person Blastfurnace    schedule 19.02.2013
comment
Это известный критерий делимости на 9: en.wikipedia.org/wiki/Divisibility_rule   -  person Bogdan    schedule 19.02.2013
comment
Это должно быть математическим или мы могли бы сделать это: var a=0; b="362".split(''); for (i in b) {a += Number(b[i])};   -  person גלעד ברקן    schedule 19.02.2013
comment
@groovy Это круто! Я не думал о создании массива! и нет, это не должно быть математическим.   -  person Albert Renshaw    schedule 19.02.2013
comment
@ Богдан Я предполагал, что это уже хорошо известно.   -  person Albert Renshaw    schedule 19.02.2013


Ответы (3)


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

Учитывая, что 27 — это 00011011 в виде 8-битного двоичного кода, процесс...

00010001 + 00000101 = 00010110  <-  every other digit step
00010010 + 00000001 = 00010011  <-  pairs of digits
00000011 + 00000001 = 00000100  <-  quads, giving final result 4

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

02040608 + 01030507 = 03071115  <-  every other digit
00070015 + 00030011 = 00100026  <-  pairs
00000026 + 00000010 = 00000036  <-  quads, final result

Таким образом, 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 = 36, что правильно, но вы можете сделать это эффективно, только если ваше числовое представление является десятичным с фиксированной шириной. Это всегда занимает lg(n) итераций, где lg означает логарифм по основанию два, и вы округляете в большую сторону.

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

Если вы считаете однозначные сложения, то на самом деле здесь больше работы, чем простой цикл. Идея, как и в случае с побитовым приемом для подсчета битов, состоит в том, чтобы переупорядочить эти сложения (используя ассоциативность), а затем вычислить как можно больше параллельно, используя одно сложение полной ширины для реализации двух сложений половинной ширины, четырех сложений. добавление четверти ширины и т. д. Существуют значительные накладные расходы для операций очистки и смещения цифр, и даже больше, если вы реализуете это как цикл (вычисление или поиск значений маскирования цифр и расстояния сдвига для каждого шага). «Петля», вероятно, должна быть полностью развернута, а эти маски и расстояния сдвига должны быть включены в код как константы, чтобы избежать этого.

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

Одна из проблем заключается в том, что в наши дни поддержка BCD довольно редка. Раньше это было довольно распространено в 8-битные и 16-битные дни, но, насколько мне известно, процессоры, которые все еще поддерживают его, теперь делают это в основном для обратной совместимости. Причины включают...

  1. Самые ранние процессоры не включали аппаратное умножение и деление. Аппаратная поддержка этих операций означает, что теперь проще и эффективнее преобразовывать двоичные числа в десятичные. Двоичные файлы сейчас используются почти везде, а BCD в основном забыты.

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

  3. По мере увеличения чисел даже упакованные BCD упаковываются довольно неэффективно. Представления чисел с основанием 10^x обладают наиболее важными свойствами основания 10 и легко декодируются как десятичные. Базе 1000 требуется только 10 бит на три цифры, а не 12, потому что 2 ^ 10 равно 1024. Этого достаточно, чтобы показать, что вы получаете дополнительную десятичную цифру для 32 бит — 9 цифр вместо 8 — и у вас все еще осталось 2 бита. , например для знакового бита.

Дело в том, что для того, чтобы этот алгоритм суммирования цифр вообще был полезен, вам нужно работать с десятичными числами фиксированной ширины, вероятно, не менее 32 бит (8 цифр). Это дает 12 операций (6 масок, 3 сдвига, 3 сложения), а не 15 сложений для (полностью развернутого) простого цикла. Однако это пограничный выигрыш, и другие проблемы в коде могут легко означать, что он на самом деле медленнее.

Прирост эффективности более заметен при 64-битном (16 десятичных разрядов), поскольку по-прежнему остается только 16 операций (8 масок, 4 сдвига, 4 сложения), а не 31, но шансы найти процессор, поддерживающий 64-битные операции BCD, кажутся незначительными. И даже если бы вы это сделали, как часто вам это нужно в любом случае? Кажется маловероятным, что это может стоить усилий и потери мобильности.

person Steve314    schedule 19.02.2013
comment
1+2+3+4+5+6+7+8 -> 7 операций сложения. Ваш -› 8 операций сложения. Просто говорю... - person Bernhard Barker; 19.02.2013
comment
@Dukeling - как обычно, асимптотический выигрыш на самом деле является выигрышем только тогда, когда ваша проблема достаточно велика. Если ваши числа с фиксированной шириной имеют достаточно большую ширину, вы выиграете от того, что они будут O (log n), а не O (n). Но я испортил эти примеры в первый раз вручную, как есть. Если вы знаете, что все ваши числа будут достаточно малы, самым быстрым решением будет справочная таблица предварительно вычисленных результатов. Вы даже можете использовать это, чтобы оптимизировать это, работая с базой 100 или базой 10000 в начале, фактически минуя пару итераций. - person Steve314; 19.02.2013
comment
@Dukeling - при этом в моем примере есть только три операции сложения для 8 цифр - просто говорю ;-) - person Steve314; 19.02.2013
comment
* однозначные операции сложения. По общему признанию, не самый важный (но как только мы выходим за пределы размера наибольшего целочисленного примитива в языках высокого уровня, он имеет значение). В языках высокого уровня вам, вероятно, будет сложно добиться большей эффективности, чем простое добавление отдельных цифр. - person Bernhard Barker; 19.02.2013
comment
@Dukeling - хорошо, вы правы - это имело бы смысл только с аппаратным десятичным представлением с фиксированной шириной, в соответствии с теми десятичными числами с двоичным кодом, которые раньше поддерживались процессорами. Я сомневаюсь, что они беспокоятся в наши дни, за исключением, возможно, обратной совместимости. И даже тогда вам нужно учитывать другие дополнительные операции (маскирование и сдвиг), так что на самом деле в этом нет никакой практической ценности - аппаратное BCD с шестнадцатью с лишним цифрами будет очень трудно найти. - person Steve314; 19.02.2013
comment
@Dukeling - на самом деле, если подумать, здесь всегда должно выполняться одно и то же количество добавлений, выполняемых простой итерацией - вместе с другими добавлениями и другими операциями - просто используя ассоциативность добавления для их изменения. Суть побитового трюка заключается в параллельном запуске нескольких дополнений, например. использование 8-битного сложения для получения эффекта четырех двухбитных сложений или двух четырехбитных сложений. - person Steve314; 19.02.2013

Вот что-то в Haskell:

sumDigits n = 
  if n == 0 
     then 0
     else let a = mod n 10
          in a + sumDigits (div n 10)

О, но я только что прочитал, что ты уже делаешь это...

(тогда есть и очевидное:

sumDigits n = sum $ map (read . (:[])) . show $ n

)

person גלעד ברקן    schedule 19.02.2013

Для короткого кода попробуйте следующее:

int digit_sum(int n){
    if (n<10) return n;
    return n%10 + digit_sum(n/10);
}

Или, на словах,

-Если число меньше десяти, то сумма цифр и есть само число.

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

-Этот алгоритм также можно обобщить для любого основания, заменив основание вместо 10.

person user2016670    schedule 04.04.2013