В одном форуме прочитал такую задачку. Дано N. Необходимо посчитать sum(N/i), где i от 1 до N. Деление целочисленное. Например для 10: 10/1 = 10 10/2 = 5 10/3 = 3 10/4 = 2 10/5 = 2 10/6 = 1 10/7 = 1 10/8 = 1 10/9 = 1 10/10 = 1 Итого: 10+5+3+2+2+1+1+1+1+1 = 27 Требуется написать максимально быструю программу. Максимальное значение N - 10^12.
Daniil Ну во превых можно сократить вычисления в два раза: все результаты полученные целочисленным делением на числа >= (N%2)+1 всегда будут 1
А я блин поизвращался: Buffer db MAX dup(0) mov eax,N mov bl,I mov esi,offset buffer mov edi,esi round: div bl mov word ptr[esi],eax ; частное test edx,edx ; есть ли остаток jne short next jmps exit_round next: mov eax,edx add esi,4 jmp round exit_round: sub esi,4 add edx,word ptr[esi] test edi,esi jne exit_round
Код (Text): sum:;(esp +4 - i64) fldcw [.cwnear] fild qword [esp+4] ;N fld st0 fsqrt ;N sqrt(N) fldcw [.cwdown] frndint sub esp,8 fist dword [esp] ;int(sqrt(N)) fmul st0,st0 fchs ;N sum=-int(sqrt(N))^2 .l0: fild dword [esp] ;N sum i fdivr st0,st2 ;N sum N/i frndint fadd st1,st0 ;N sum+N/i N/i faddp st1,st0 ;N sum+2N/i dec dword [esp] jnz .l0 fstp st1 fistp qword [esp] pop eax pop edx ret 8 .cwnear dw 33Fh .cwdown dw 73Fh
В принципе, вариант Black_mirror неплохо решает задачу алгоритмически, но конечно не "максимально быстро" в смысле вычислений. Можно попытаться пооптимизировать вычисления для больших N. Если брать N хотя бы 1000 и более, то увидим что при уменьшении i от исходного значения int(sqrt(N)) значения k=int(N/i) меняются медленно и вполне предсказуемо: сначала на 1, потом начинается "вкрапление" 2, потом устойчивая разница на 2 и т.д. Поэтому идея заключается в том, чтобы при больших начальных i заменить длительную операцию деления (на пеньках, что div, что fdiv+frndint - не менее 60 тактов) на целочисленные операции add\sub\cmp для получения очередного k. Наиболее просто задачка решается для разности соседних k < 3, а это около 30% от общего числа итераций, т.к. N/(i-1)-N/i > 2 при i < sqrt(N/2) ~ 0.7*sqrt(N). Пусть на входе имеем Код (Text): i = int(sqrt(N)) k = i r = N-i*i - остаток от деления N/i Тогда на следующем шаге для i=i-1 будем иметь Код (Text): r=r+k ;остаток при том же k, т.к.r=N-(i-1)*k =(N-i*k)+k inc k ;прогнозируемое значение k r=r-i ;остаток при k=k+1 cmp r,i ;проверяем не нужно ли еще увеличить k jl @@sum inc k r=r-i ;если развивать идею, то здесь в принципе можно вставить еще проверку на r >= i ;и либо еще увеличивать k, либо найти его делением исходного r на i @@sum: sum=sum+k dec i Цикл выполняется до некоторого порогового i, затем переход на прямое вычисление по Black_mirror. При ограниченном i только вычисление sum требует 64битных чисел, остальные операции с r32.
А если считать таблицу первых M значений выше описаной суммы? ИМХО задачка поинтереснее и потруднее . Тоесть, например, для M=10 ответ будет (1,3,5,8,10,14,16,20,23,27)