Как подсчитать каждую цифру в диапазоне целых чисел?

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

  • от 1 до 100
  • от 51 до 300
  • от 1 до 2000 с нулями слева

Очевидным решением является выполнение цикла от первого до последнего числа, преобразование счетчика в строку с нулями или без них слева, извлечение каждой цифры и использование ее в качестве индекса для увеличения массива из 10 целых чисел.

Интересно, есть ли лучший способ решить эту проблему, без необходимости цитировать весь диапазон целых чисел.

Разрешения на любом языке или псевдокоде приветствуются.


Изменить:

Обзор отзывов
Джон в CashCommons и Уэйн Конрад отмечают, что мой нынешний подход хорош и достаточно быстр. Позвольте мне использовать глупую аналогию: если вам была дана задача подсчета квадратов на шахматной доске менее чем за 1 минуту, вы могли бы закончить задачу, посчитав квадраты один за другим, но лучшим решением будет подсчет сторон и сделайте умножение, потому что позже вас могут попросить подсчитать плитки в здании.
Алекс Рейснер указывает на очень интересный математический закон, который, к сожалению, кажется, не имеет отношения к этой проблеме.
Андрес предлагает использовать тот же алгоритм, который использует Im, но извлекает цифры с помощью% 10 вместо подстрок.
John at CashCommons и phord предлагают предварительное вычисление требуемых цифр и их хранение в справочной таблице или для сырой скорости - массив. Это могло бы быть хорошим решением, если бы у нас было абсолютное, неизменяемое, установленное в камне, максимальное целочисленное значение. Я никогда не видел одного из них. Высокопроизводительный знак и сетчатый фильтр рассчитали необходимые цифры для разных диапазонов. Результат за один миллион, по-видимому, указывает, что есть доля, но результаты для другого числа показывают разные пропорции.
фильтр нашел некоторые формулы, которые можно использовать для подсчета цифры для числа, которое составляет десять. У Роберта Харви был очень интересный опыт публикации вопроса в MathOverflow. Один из математиков написал решение, используя математическую нотацию.
Aaronach разработал и протестировал решение с использованием математики. После публикации он просмотрел формулы, возникшие из Math Overflow, и обнаружил в нем недостаток (укажите на Stackoverflow:).
Ноаклавин разработал алгоритм и представил его в псевдокоде.

Новое решение
Прочитав все ответы и сделав несколько экспериментов, я обнаружил, что для диапазона целых чисел от 1 до 10 n -1:

  • Для цифр от 1 до 9 требуются фрагменты n * 10 (n-1)
  • Для цифры 0, если не использовать начальные нули, необходимо n * 10 n-1 - ((10 n -1)/9)
  • Для цифры 0, если используются начальные нули, необходимо n * 10 n-1 - n

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

Например, если n = 6, диапазон от 1 до 999,999:

  • Для цифр с 1 по 9 нам нужно 6 * 10 5= 600 000 из каждого
  • Для цифры 0 без начальных нулей нам нужно 6 * 10 5 - (10 6 -1)/9 = 600 000 - 111 111 = 488 889
  • Для цифры 0 с начальными нулями нам нужно 6 * 10 5 - 6 = 599,994

Эти цифры можно проверить с помощью результатов высокоэффективной маркировки.

Используя эти формулы, я улучшил исходный алгоритм. Он по-прежнему находится в диапазоне от первого до последнего числа в диапазоне целых чисел, но если он найдет число, которое является степенью десяти, он использует формулы для добавления к цифрам, подсчитывая количество для полного диапазона от 1 до 9 или от 1 до 99 или от 1 до 999 и т.д. Здесь алгоритм в псевдокоде:

integer First,Last //First and last number in the range
integer Number     //Current number in the loop
integer Power      //Power is the n in 10^n in the formulas
integer Nines      //Nines is the resut of 10^n - 1, 10^5 - 1 = 99999
integer Prefix     //First digits in a number. For 14,200, prefix is 142
array 0..9  Digits //Will hold the count for all the digits

FOR Number = First TO Last
  CALL TallyDigitsForOneNumber WITH Number,1  //Tally the count of each digit 
                                              //in the number, increment by 1
  //Start of optimization. Comments are for Number = 1,000 and Last = 8,000.
  Power = Zeros at the end of number //For 1,000, Power = 3
  IF Power > 0                       //The number ends in 0 00 000 etc 
    Nines = 10^Power-1                 //Nines = 10^3 - 1 = 1000 - 1 = 999
    IF Number+Nines <= Last            //If 1,000+999 < 8,000, add a full set
      Digits[0-9] += Power*10^(Power-1)  //Add 3*10^(3-1) = 300 to digits 0 to 9
      Digits[0]   -= -Power              //Adjust digit 0 (leading zeros formula)
      Prefix = First digits of Number    //For 1000, prefix is 1
      CALL TallyDigitsForOneNumber WITH Prefix,Nines //Tally the count of each 
                                                     //digit in prefix,
                                                     //increment by 999
      Number += Nines                    //Increment the loop counter 999 cycles
    ENDIF
  ENDIF 
  //End of optimization
ENDFOR  

SUBROUTINE TallyDigitsForOneNumber PARAMS Number,Count
  REPEAT
    Digits [ Number % 10 ] += Count
    Number = Number / 10
  UNTIL Number = 0

Например, для диапазона от 786 до 3,021 счетчик будет увеличен:

  • В 1 от 786 до 790 (5 циклов)
  • К 9 от 790 до 799 (1 цикл)
  • В 1 от 799 до 800
  • К 99 от 800 до 899
  • В 1 от 899 до 900
  • К 99 от 900 до 999
  • В 1 от 999 до 1000
  • К 999 от 1000 до 1999
  • К 1 году с 1999 по 2000 год
  • К 999 году с 2000 по 2999 год.
  • В 1 от 2999 до 3000
  • В 1 от 3000 до 3010 (10 циклов)
  • В 9 от 3010 до 3019 (1 цикл)
  • В 1 от 3019 до 3021 (2 цикла)

Всего: 28 циклов Без оптимизации: 2,235 циклов

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

Если требуется диапазон от 700 до 1000 с начальными нулями, используйте алгоритм для 10 700-11 000, а затем вычитайте 1000 - 700 = 300 из числа цифр 1.

Исходный код и исходный код

Я проверил оригинальный подход, тот же подход с использованием% 10 и нового решения для некоторых больших диапазонов, с этими результатами:

Original             104.78 seconds
With %10              83.66
With Powers of Ten     0.07

Снимок экрана тестового приложения:
alt text http://clarion.sca.mx/images/stories/digitsbench.png

Если вы хотите увидеть полный исходный код или запустить тест, используйте следующие ссылки:

Принятый ответ

noahlavine решение может быть правильным, но я просто не мог следовать псевдокоду, я думаю, что некоторые детали отсутствуют или не полностью объяснены.

Решение Aaronaught кажется правильным, но код слишком сложный для моего вкуса.

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

Ответ 1

Чтобы наматывать цифры из числа, нам потребовалось бы только дорогостоящее преобразование строк, если мы не смогли бы сделать мод, цифры могут быть быстро скопированы из числа, подобного этому:

feed=number;
do
{ digit=feed%10;
  feed/=10; 
  //use digit... eg. digitTally[digit]++;
  }
while(feed>0)

этот цикл должен быть очень быстрым и может быть просто помещен внутри цикла от начала до конца для простейшего способа подсчета цифр.

Чтобы двигаться быстрее, для большего диапазона чисел, im ищет оптимизированный метод подсчета всех цифр от 0 до числа * 10 ^ значение (от начала до конца bazzogles me)

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

  1 10 100 1000 10000 2 20 30 40 60 90 200 600 2000  6000

0 1 1  10  190  2890  1  2  3  4  6  9  30 110  490  1690
1 0 1  20  300  4000  1 12 13 14 16 19 140 220 1600  2800
2 0 1  20  300  4000  0  2 13 14 16 19  40 220  600  2800
3 0 1  20  300  4000  0  2  3 14 16 19  40 220  600  2800
4 0 1  20  300  4000  0  2  3  4 16 19  40 220  600  2800
5 0 1  20  300  4000  0  2  3  4 16 19  40 220  600  2800
6 0 1  20  300  4000  0  2  3  4  6 19  40 120  600  1800
7 0 1  20  300  4000  0  2  3  4  6 19  40 120  600  1800
8 0 1  20  300  4000  0  2  3  4  6 19  40 120  600  1800
9 0 1  20  300  4000  0  2  3  4  6  9  40 120  600  1800

edit: очистка моего оригинала мысли:

из таблицы грубой силы, показывающей от 0 (включительно) до poweroTen (notinc) видно, что majordigit of tenpower:

increments tally[0 to 9] by md*tp*10^(tp-1)
increments tally[1 to md-1] by 10^tp
decrements tally[0] by (10^tp - 10) 
(to remove leading 0s if tp>leadingzeros)
can increment tally[moresignificantdigits] by self(md*10^tp) 
(to complete an effect)

если эти корректировки талонов были применены для каждой значащей цифры, счет должен быть изменен, как если бы считалось от 0 до конца-1

настройки могут быть инвертированы, чтобы удалить предыдущий диапазон (начальный номер)

Спасибо Aaronaught за ваш полный и проверенный ответ.

Ответ 2

Там есть ясное математическое решение такой проблемы. Предположим, что значение равно нулю с максимальным количеством цифр (это не так, но мы компенсируем это позже), и объясните это через:

  • От 0 до 9 каждая цифра появляется один раз
  • От 0 до99 каждая цифра происходит 20 раз (10x в позиции 1 и 10x в позиции 2)
  • Начиная с 0-999, каждая цифра встречается 300 раз (100x в P1, 100x в P2, 100x в P3)

Очевидный шаблон для любой заданной цифры, если диапазон от 0 до степени 10, N * 10 N-1, где N - мощность от 10.

Что делать, если диапазон не равен 10? Начните с наименьшей мощности 10, затем обработайте. Самый простой случай - максимум 399. Мы знаем, что для каждого кратного 100 каждая цифра имеет не менее 20 раз, но мы должны компенсировать количество раз, которое она появляется в наиболее значимая цифра, которая будет равна 100 для цифр 0-3 и ровно равна нулю для всех остальных цифр. В частности, дополнительная сумма для добавления составляет 10 N для соответствующих цифр.

Вводя это в формулу, для верхних границ, которые на 1 меньше кратного мощности 10 (т.е. 399, 6999 и т.д.), она становится: M * N * 10 N-1 + iif (d <= M, 10 N 0)

Теперь вам нужно иметь дело с остатком (который мы назовем R). Возьмите 445 в качестве примера. Это то, что результат для 399, плюс диапазон 400-445. В этом диапазоне MSD имеет место R больше раз, и все цифры (включая MSD) также встречаются на тех же частотах, которые они будут от диапазона [0 - R].

Теперь нам нужно только компенсировать начальные нули. Этот шаблон прост - просто:

10 N + 10 N-1 + 10 N-2 +... + ** 10 0

Обновление: Эта версия правильно учитывает "нулевые корни", т.е. нули в средних позициях при работе с остатком ([4 0 0, 4 0 1, 4 0 2,...]). Вычисление нулей заполнения немного уродливое, но исправленный код (псевдокод C-стиля) обрабатывает его:

function countdigits(int d, int low, int high) {
    return countdigits(d, low, high, false);
}

function countdigits(int d, int low, int high, bool inner) {
    if (high == 0)
        return (d == 0) ? 1 : 0;

    if (low > 0)
        return countdigits(d, 0, high) - countdigits(d, 0, low);

    int n = floor(log10(high));
    int m = floor((high + 1) / pow(10, n));
    int r = high - m * pow(10, n);
    return
        (max(m, 1) * n * pow(10, n-1)) +                             // (1)
        ((d < m) ? pow(10, n) : 0) +                                 // (2)
        (((r >= 0) && (n > 0)) ? countdigits(d, 0, r, true) : 0) +   // (3)
        (((r >= 0) && (d == m)) ? (r + 1) : 0) +                     // (4)
        (((r >= 0) && (d == 0)) ? countpaddingzeros(n, r) : 0) -     // (5)
        (((d == 0) && !inner) ? countleadingzeros(n) : 0);           // (6)
}

function countleadingzeros(int n) {
      int tmp= 0;
      do{
         tmp= pow(10, n)+tmp;
         --n;
         }while(n>0);
         return tmp;
         }

function countpaddingzeros(int n, int r) {
    return (r + 1) * max(0, n - max(0, floor(log10(r))) - 1);
}

Как вы можете видеть, это получило немного уродливое, но оно все еще работает в O (log n), поэтому, если вам нужно обрабатывать числа в миллиардах, это все равно даст вам мгновенные результаты.:-) И если вы запустите его на диапазоне [0 - 1000000], вы получите то же самое распределение, что и сообщение, размещенное высокоэффективной меткой, поэтому я почти уверен, что это правильно.

FYI, причина для переменной inner заключается в том, что функция начального нуля уже рекурсивна, поэтому ее можно пересчитать только при первом выполнении countdigits.

Обновление 2: В случае, если код трудно прочитать, здесь ссылка на то, что означает каждая строка выражения return countdigits (я пробовал встроенные комментарии, но они сделали код еще сложнее чтения):

  • Частота любой цифры до максимальной мощности 10 (0-99 и т.д.).
  • Частота MSD выше любого кратного максимальной мощности 10 (100-399)
  • Частота любых цифр в остатке (400-445, R = 45)
  • Дополнительная частота MSD в остатке
  • Количество нулей в среднем положении для диапазона остатков (404, 405...)
  • Вычитайте начальные нули только один раз (по самому внешнему циклу)

Ответ 3

Я предполагаю, что вам нужно решение, в котором числа находятся в диапазоне, и у вас есть начальный и конечный номер. Представьте, начиная с стартового номера и подсчета до тех пор, пока вы не дойдете до конечного числа - он будет работать, но он будет медленным. Я думаю, что трюк с быстрым алгоритмом заключается в том, чтобы понять, что для того, чтобы подняться на одну цифру в месте 10 ^ x и сохранить все остальное одинаково, вам нужно использовать все цифры перед ним 10 ^ х раз плюс все цифры 0 -9 10 ^ (x-1) раз. (За исключением того, что ваш подсчет, возможно, связан с переносом мимо x-й цифры - я исправлю это ниже).

Вот пример. Скажем, вы считаете от 523 до 1004.

  • Сначала вы считаете от 523 до 524. Это использует цифры 5, 2 и 4 раз.
  • Во-вторых, считайте от 524 до 604. Самая правая цифра выполняет 6 циклов по всем цифрам, поэтому вам нужно 6 копий каждой цифры. Вторая цифра проходит через цифры от 2 до 0, по 10 раз. Третья цифра - 6 5 раз и 5 100-24 раза.
  • В-третьих, подсчет от 604 до 1004. Самая правая цифра составляет 40 циклов, поэтому добавьте 40 копий каждой цифры. Второй из правых цифр выполняет 4 цикла, поэтому добавьте 4 копии каждой цифры. Самая левая цифра составляет 100 каждый из 7, 8 и 9, плюс 5 из 0 и 100 - 5 из 6. Последняя цифра 1 раз.

Чтобы ускорить последний бит, посмотрите на часть о самых правых двух местах. Он использует каждую цифру 10 + 1 раз. В общем случае 1 + 10 +... + 10 ^ n = (10 ^ (n + 1) - 1)/9, что мы можем использовать для ускорения счета даже больше.

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

Теперь предположим, что псевдокод считается как язык. Вот, вот что я буду делать:

convert start and end numbers to digit arrays start[] and end[]
create an array counts[] with 10 elements which stores the number of copies of
     each digit that you need

iterate through start number from right to left. at the i-th digit,
    let d be the number of digits you must count up to get from this digit
        to the i-th digit in the ending number. (i.e. subtract the equivalent
        digits mod 10)
    add d * (10^i - 1)/9 to each entry in count.
    let m be the numerical value of all the digits to the right of this digit,
        n be 10^i - m.
    for each digit e from the left of the starting number up to and including the
        i-th digit, add n to the count for that digit.
    for j in 1 to d
        increment the i-th digit by one, including doing any carries
        for each digit e from the left of the starting number up to and including
            the i-th digit, add 10^i to the count for that digit
    for each digit e from the left of the starting number up to and including the
        i-th digit, add m to the count for that digit.
    set the i-th digit of the starting number to be the i-th digit of the ending
        number.

Oh, и так как значение я увеличивается на единицу каждый раз, отследите свой старый 10 ^ я и просто умножьте его на 10, чтобы получить новый, вместо того, чтобы каждый раз увеличивать уровень.

Ответ 4

Вот очень плохой ответ, мне стыдно публиковать его. Я попросил Mathematica подсчитать цифры, используемые во всех числах от 1 до 1 000 000, без ведущих 0. Вот что я получил:

0   488895
1   600001
2   600000
3   600000
4   600000
5   600000
6   600000
7   600000
8   600000
9   600000

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

Ответ 5

I задал этот вопрос по Math Overflow и получил отговорку, задав такой простой вопрос. Один из пользователей пожалел меня и сказал, что если я разместил его на "Искусство решения проблем" , он ответил бы на него; поэтому я и сделал.

Вот ответ, который он опубликовал:
http://www.artofproblemsolving.com/Forum/viewtopic.php?p=1741600#1741600

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

С яркой стороны уравнение является рекурсивным, поэтому должно быть простым делом превратить его в рекурсивную функцию с несколькими строками кода, тем, кто понимает математику.

Ответ 6

Твой подход прекрасен. Я не уверен, почему вам понадобится что-нибудь быстрее, чем то, что вы описали.

Или это даст вам мгновенное решение: прежде чем он вам действительно понадобится, вычислите, что вам нужно от 1 до некоторого максимального числа. Вы можете хранить номера, необходимые на каждом шаге. Если у вас есть диапазон, подобный вашему второму примеру, это будет то, что нужно от 1 до 300, минус то, что нужно для 1-50.

Теперь у вас есть таблица поиска, которую можно вызвать по желанию. Выполнение до 10 000 займет всего несколько МБ и, что, несколько минут, чтобы вычислить, один раз?

Ответ 7

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

На самом деле это довольно просто, но не просто объяснить.

Если вы выберете первые n чисел

     1
     2
     3

     .
     .
     .


     9
    10
    11

Обычно начинается отсчет цифр, необходимых от номера начальной комнаты, до номера конечной комнаты в порядке слева направо, так что для вышеизложенного мы имеем один 1, один 2, один 3... один 9, два 1 один ноль, четыре и т.д. Большинство решений, которые я видел, использовали этот подход с некоторой оптимизацией, чтобы ускорить его.

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

Легче визуализировать, если вы используете Excel для записи чисел, но используйте отдельный столбец для каждой цифры номера

     A    B    C
     -    -    -
     0    0    1  (assuming room numbers do not start at zero)
     0    0    2
     0    0    3
     .
     .
     .
     3    6    4
     3    6    5
     .
     .
     .

     6    6    9
     6    7    0
     6    7    1

     ^
     sum in columns not rows

Итак, если самый высокий номер комнаты равен 671, столбец сотен будет иметь 100 нулей по вертикали, а затем 100 единиц и т.д. до 71 шестерки, если необходимо, игнорируйте 100 нулей, поскольку мы знаем, что все они ведущие.

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

Итак, нет циклов, все вычисляется с помощью деления. Я использую рекурсию для перемещения "вверх" столбцов до тех пор, пока не будет достигнут максимальный (в данном случае сотни), а затем вернется вниз, общаясь по мере его прохождения.

Я написал это на С# и могу опубликовать код, если кто-то заинтересован, не выполнил никаких контрольных сроков, но он по существу мгновен для значений до 10 ^ 18 номеров.

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

Ответ 8

Это не отвечает на ваш точный вопрос, но интересно отметить распределение первых цифр в соответствии с Benford Law. Например, если вы выбираете набор чисел случайным образом, 30% из них будут начинаться с "1", что несколько противоречит интуиции.

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

Ответ 9

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

#!/usr/bin/ruby1.8

def digits_for_range(min, max, leading_zeros)
  bins = [0] * 10
  format = [
    '%',
    ('0' if leading_zeros),
    max.to_s.size,
    'd',
  ].compact.join
  (min..max).each do |i|
    s = format % i
    for digit in s.scan(/./)
      bins[digit.to_i] +=1  unless digit == ' '
    end
  end
  bins
end

p digits_for_range(1, 49, false) 
# => [4, 15, 15, 15, 15, 5, 5, 5, 5, 5]

p digits_for_range(1, 49, true)
# => [13, 15, 15, 15, 15, 5, 5, 5, 5, 5]

p digits_for_range(1, 10000, false)
# => [2893, 4001, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000]

Ruby 1.8, язык, известный как "собака медленный", запускает код выше 0.135 секунд. Это включает загрузку интерпретатора. Не отказывайтесь от очевидного алгоритма, если вам не нужна более высокая скорость.

Ответ 10

Если вам нужна сырая скорость для многих итераций, попробуйте таблицу поиска:

  • Создайте массив с двумя размерами: 10 x max-house-number

    int nDigits[10000][10] ;   // Don't try this on the stack, kids!
  1. Заполните каждую строку цифрами, необходимыми для получения этого числа от нуля.
    Подсказка: используйте предыдущую строку как начало:

    n=0..9999:
       if (n>0) nDigits[n] = nDigits[n-1]
       d=0..9:
           nDigits[n][d] += countOccurrencesOf(n,d)   // 
  1. Number of digits "between" two numbers becomes simple subtraction.
       For range=51 to 300, take the counts for 300 and subtract the counts for 50.
       0 = nDigits[300][0] - nDigits[50][0]
       1 = nDigits[300][1] - nDigits[50][1]
       2 = nDigits[300][2] - nDigits[50][2]
       3 = nDigits[300][3] - nDigits[50][3]
       etc.

Ответ 11

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

Но если это не то, что вы ищете, можете ли вы дать лучший пример?

Отредактировано:

Теперь я думаю, что у меня проблема. Я думаю, вы можете считать это (псевдо C):

int histogram[10];
memset(histogram, 0, sizeof(histogram));

for(i = startNumber; i <= endNumber; ++i)
{
    array = separateDigits(i);
    for(j = 0; k < array.length; ++j)
    {
        histogram[k]++;
    }
}

Отдельные цифры реализуют функцию в ссылке.

Каждая позиция гистограммы будет содержать количество каждой цифры. Например

histogram[0] == total of zeros
histogram[1] == total of ones

...

Привет