Предположите продажу тех металлических цифр, используемых для нумерации зданий, дверей блокировщика, гостиничных номеров, и т.д. Необходимо найти, сколько из каждой цифры для поставки, когда клиент должен пронумеровать двери/здания:
Очевидное решение состоит в том, чтобы сделать цикл сначала к последнему числу, преобразовать в противоречии со строкой с или без нулей налево, извлечь каждую цифру и использовать его в качестве индекса для постепенного увеличения массива 10 целых чисел.
Интересно, существует ли лучший способ решить это, не имея необходимость циклично выполняться через весь целочисленный диапазон.
Решения на любом языке или псевдокоде приветствуются.
Обзор ответов
John в CashCommons и Wayne Conrad комментируют, что мой текущий подход хорош и достаточно быстр. Позвольте мне использовать глупую аналогию: при предоставлении задачи подсчета квадратов в шахматной доске меньше чем за 1 минуту Вы могли закончить задачу путем подсчета квадратов один за другим, но лучшее решение состоит в том, чтобы считать стороны и сделать умножение, потому что Вас позже можно попросить считать мозаики в здании.
Alex Reisner указывает на очень интересный математический закон, что, к сожалению, кажется, не относится к этой проблеме.
Andres предлагает тот же алгоритм, который я использую, но извлекаю цифры с %10 операциями вместо подстрок.
John в CashCommons и phord предлагает предварительно вычислить требуемые цифры и сохранить их в справочной таблице или, для необработанной скорости, массива. Это могло быть хорошим решением, если бы у нас был абсолют, неперемещаемый, установленный в камне, максимальном целочисленном значении. Я никогда не видел одного из тех.
Высокоэффективный Mark и сито вычислили необходимые цифры для различных диапазонов. Результат для одного millon, кажется, указывает, что существует пропорция, но результаты для другого числа показывают различные пропорции.
сито нашло некоторые формулы, которые могут использоваться для подсчета цифры для числа, которые являются питанием десять. У Robert Harvey был очень интересный опыт при регистрации вопроса в MathOverflow. Один из математических парней записал решение с помощью математической нотации.
Aaronaught разработал и протестировал решение с помощью математики. После регистрации его он рассмотрел формулы, порожденные из Математического Переполнения, и нашел дефект в нем (укажите на Stackoverflow :).
noahlavine разработал алгоритм и представил его в псевдокоде.
Новое решение
После чтения всех ответов и выполнения некоторых экспериментов, я нашел что для диапазона целого числа от 1 до 10n-1:
Первая формула была найдена ситом (и вероятно другими), и я нашел другие два методом проб и ошибок (но они могут быть включены в другие ответы).
Например, если n = 6, диапазон 1 - 999 999:
Эти числа могут быть проверены с помощью Высокоэффективных результатов Mark.
Используя эти формулы, я улучшил исходный алгоритм. Это все еще циклично выполняется сначала к последнему числу в диапазоне целых чисел, но, если это находит число, которое является питанием десять, это использует формулы для добавления к количеству цифр количества для полного спектра 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, счетчик будет увеличен:
Общее количество: 28 циклов Без оптимизации: 2 235 циклов
Обратите внимание, что этот алгоритм решает проблему без начальных нулей. Для использования его с начальными нулями я использовал взлом:
Если диапазон 700 - 1 000 с начальными нулями необходим, используйте алгоритм для 10 700 - 11 000 и затем substract 1,000 - 700 = 300 от количества цифры 1.
Сравнительный тест и Исходный код
Я протестировал исходный подход, тот же подход с помощью %10 и новое решение для некоторых больших спектров, с этими результатами:
Original 104.78 seconds With %10 83.66 With Powers of Ten 0.07
Снимок экрана приложения сравнительного теста:
(источник: clarion.sca.mx)
Если требуется видеть полный исходный код или выполнить сравнительный тест, используйте эти ссылки:
Принятый ответ
решение для noahlavine может быть правильным, но l просто не мог следовать псевдо коду, я думаю, что существуют некоторые пропавшие без вести деталей или не полностью объяснены.
Решение Aaronaught, кажется, корректно, но код просто слишком сложен для моего вкуса.
Я принял ответ сита, потому что его ход мыслей вел меня для разработки этого нового решения.
Код:
(assoc 'window-id (frame-parameters (selected-frame)))
даст вам идентификатор окна для X11. Я не уверен в других платформах. Как только у вас есть это, вы можете сделать вещи в окне из любого другого приложения X вы хотите.
Если вы хотите использовать весь код Emacs Lisp, вам нужно будет использовать интерфейс xlib, который был принят в xwem, который является оконным менеджером, написанным на Emacs Lisp.
http://savannah.nongnu.org/projects/xwem
-121--3193790-Полагаю, вам не хватает ссылки на нужную библиотеку. Убедитесь, что библиотека CUDA добавлена по пути «Свойства конфигурации- > Связь - > Ввод». См. this .
-121--2929478-Чтобы вытащить цифры из числа, нам нужно было бы только сделать дорогостоящее преобразование последовательности, если бы мы не могли сделать mod, цифры можно было бы быстрее всего вытолкнуть из числа, как это:
feed=number;
do
{ digit=feed%10;
feed/=10;
//use digit... eg. digitTally[digit]++;
}
while(feed>0)
этот цикл должен быть очень быстрым и может быть просто помещен в цикл от начальных до конечных чисел для простейшего способа подсчета цифр.
Для более быстрого перехода к большему диапазону чисел требуется оптимизированный метод подсчета всех цифр от 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
изменить: очистить мои исходные мысли:
из таблицы грубых сил, показывающей подсчет от 0 (включено) до poweroTen (notinc) видно, что мажордигит силы:
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
, корректировки могут быть инвертированы, чтобы удалить предшествующий диапазон (начальный номер)
Спасибо Ааронахту за ваш полный и проверенный ответ.
Ваш подход в порядке. Я не уверен, почему вам когда-нибудь понадобится ничего быстрее, чем то, что вы описали.
Или это даст вам мгновенное решение: до того, как вам действительно нужно, рассчитайте, что вам нужно от 1 до некоторого максимального номера. Вы можете хранить номера, необходимые на каждом шаге. Если у вас есть диапазон, как ваш второй пример, это было бы то, что нужно для от 1 до 300, минус, что нужно для от 1 до 50.
Теперь у вас есть таблица поиска, которую можно назвать по желанию. Делать до 10000 займет всего несколько МБ, только что несколько минут вычислить, один раз?
, Если "лучше" означает "более ясный", то я сомневаюсь относительно этого. Если бы это означает "быстрее", то да, но я не использовал бы более быстрый алгоритм вместо более ясного без насущной потребности.
#!/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 секунды. Это включает загрузку интерпретатора. Не бросайте очевидный алгоритм, если вам не нужно больше скорости.
Вы можете отделить каждую цифру ( посмотреть здесь для примера ), создайте гистограмму с записями 0..9 (который будет подсчитать, сколько цифр появилось в номере) и умножить на количество «чисел».
Но если не то, что вы ищете, можете ли вы дать лучший пример?
Отредактировано:
Теперь я думаю, что я получил проблему. Я думаю, что вы можете рассчитывать (псевдо):
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
...
С уважением
Я предполагаю, что вам нужно решение, где числа находятся в диапазоне, и у вас есть начальное и конечное число. Представьте себе, что вы начинаете со стартового числа и отсчитываете до тех пор, пока не достигнете конечного числа - это сработает, но будет медленно. Думаю, хитрость быстрого алгоритма состоит в том, чтобы понять, что для того, чтобы подняться на одну цифру в месте 10^x и сохранить все остальное неизменным, нужно использовать все цифры до нее 10^x раз плюс все цифры 0-9 10^(x-1) раз. (За исключением того, что ваш подсчет мог быть связан с прохождением x-ой цифры - я исправлю это ниже)
Вот пример. Скажем, вы считаете от 523 до 1004.
Для ускорения последнего бита посмотрите на часть о двух крайних правых местах. Каждая цифра используется 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.
О, а так как значение i увеличивается на единицу каждый раз, следите за своими старыми 10^i и просто умножайте их на 10, чтобы получить новое, вместо того, чтобы каждый раз объяснять.
Вы должны проверить Effect Games .
Это бесплатный онлайн-сервер Javascript Game Engine, который работает невероятно гладко и поддерживает все основные браузеры. Не верите мне? Воспроизвести демонстрацию или одну .
Те, которые говорят, что вы должны использовать Flash и Javascript слишком медленно, очевидно, не обращали внимания, особенно на новый браузер, такой как Chrome, который использует скомпилированный javascript . Зачем использовать Flash, когда вы можете иметь кроссплатформенную, кросс-браузерную игру, разработанную с использованием собственных функций браузера.
Вы также должны проверить Chrome Experiments , чтобы увидеть, насколько улучшились текущие возможности Javascript (а иногда и HTML5).
-121--3566054-Effect Games предоставляет бесплатные, онлайн инструменты для создания, совместного использования и играть в собственные игры на основе браузера.
Ваши игры могут включать звуковые эффекты, музыка и несколько слоев параллакс-прокручивающие плитки и спрайты.
Пользователи могут играть в ваши игры прямо в их браузеры, без необходимости новые плагины или расширения. Издать ваши игры на вашем сайте или блоге, на сайтах социальных сетей, и отправить их в наши игры раздел!
Создавайте свои игры с помощью JavaScript и наш пользовательский движок браузера игры, уровень редактор и номер люкс инструментов для разработчиков. Модуль Effect Engine поддерживает Mac OS X, Windows, Linux и все современные браузеры включая IE, Firefox, Chrome, Opera и Сафари.
То, что я делаю в этой ситуации, всегда сначала заполняет страницу содержимым на основе параметров по умолчанию того, что делает вызов Ajax. Тогда я использую ajax javascript только для обновления страницы.
-121--2600157-Вот очень плохой ответ, мне стыдно его размещать. Я попросил систему 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
В следующий раз, когда вы заказываете липкие цифры для продажи в вашем магазине оборудования, заказ в таких пропорциях, вы не будете далеко ошибаться.
Это не отвечает на ваш точный вопрос, но интересно отметить распределение первых цифр согласно Законом Бенфорда . Например, если вы выберете набор чисел наугад, 30% из них начнутся с «1», что несколько противостоит понятным.
Я не знаю о каких-либо дистрибутивах, описывающих последующие цифры, но вы можете определить это эмпирически и придумать простую формулу для вычисления приблизительно количество цифр, необходимых для любого диапазона чисел. Отказ
I задал этот вопрос о переполнении математики, и меня отшлепали за то, что я задал такой простой вопрос. Один из пользователей сжалился надо мной и сказал, что если я размещу его в The Art of Problem Solving, то он ответит, и я ответил.
Вот ответ, который он написал:
http://www.artofproblemsolving.com/Forum/viewtopic.php?p=1741600#1741600
Стыдно, но моя математика-фу недостаточна, чтобы понять, что он написал (парню 19 лет...это так удручает). Мне действительно нужно взять несколько уроков математики.
Положительным моментом является то, что уравнение является рекурсивным, так что должно быть простым делом превратить его в рекурсивную функцию с несколькими строками кода, кем-то, кто понимает математику.
Если вам нужна необработанная скорость по многим итерациям, попробуйте таблицу поиска:
int nDigits[10000][10] ; // Don't try this on the stack, kids!
n=0..9999:
if (n>0) nDigits[n] = nDigits[n-1]
d=0..9:
nDigits[n][d] += countOccurrencesOf(n,d) //
For range=51 to 300, take the counts for 300 and subtract the counts for 50. 0's = nDigits[300][0] - nDigits[50][0] 1's = nDigits[300][1] - nDigits[50][1] 2's = nDigits[300][2] - nDigits[50][2] 3's = nDigits[300][3] - nDigits[50][3] etc.
У такой проблемы есть четкое математическое решение.Предположим, что значение дополнено нулями до максимального количества цифр (это не так, но мы компенсируем это позже), и рассмотрим это:
Очевидное образец для любой данной цифры, если диапазон от 0 до степени 10, будет N * 10 N-1 , где N - степень 10.
Что, если диапазон не является степенью 10? Начните с наименьшей степени 10, затем увеличивайте. Самый простой случай - это максимальное число, например 399. Мы знаем, что для каждого числа, кратного 100, каждая цифра встречается как минимум 20 раз, но мы должны компенсировать количество раз, когда она встречается в большинстве -significant-digit position, которая будет равна 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], вы получите точно такой же дистрибутив, что и тот, который опубликовал High-Performance Mark, так что я почти уверен, что это правильно.
К вашему сведению, причина использования внутренней переменной
заключается в том, что функция ведущего нуля уже является рекурсивной, поэтому ее можно подсчитать только при первом выполнении countdigits
.
Обновление 2: Если код трудно читать, вот ссылка на то, что означает каждая строка оператора возврата countdigits
(я пробовал встроенные комментарии, но они усложнили код чтение):