Как вычислить факториал большого числа?

Тема в разделе "WASM.BEGINNERS", создана пользователем Adrax, 10 мар 2007.

  1. Adrax

    Adrax Алексей

    Публикаций:
    0
    Регистрация:
    14 окт 2006
    Сообщения:
    135
    Адрес:
    г. Курск
    Господа программисты!
    Вновь возвращаюсь к этой теме. В #30 Nouzui запостил листинг на Си. Я слегка изменил его:
    Код (Text):
    1. #include <stdio.h>
    2. unsigned short array[1000000] = {1};
    3. unsigned int len= 1;
    4. void main()
    5. {
    6. unsigned int i;
    7. unsigned long l;
    8. unsigned long cn;
    9. int N;
    10.  
    11. printf("Input N:");
    12. scanf("%d",&N);
    13.  
    14. cn= 0;
    15. for(l= 1; l<=N; l++)
    16. {
    17. for(i= 0; i<len || cn; i++)
    18. {
    19. cn+= array[i]*l;
    20. array[i]= (unsigned short)(cn%10000);
    21. cn/= 10000;
    22. }
    23. len= i;
    24. }
    25.  
    26. printf("%d", array[len-1]);
    27.  
    28. for(i= len-1; i--;)
    29. {
    30. printf("%04d", array[i]);
    31. }
    32.  
    33. printf("\n");
    34.        
    35. }
    Спасибо Nouzui - код великолепно работает
    Затем я дизассемблировал полученную программу и исходя из дизассемблерного листинга "дословно" переписал её на FASM:
    Код (Text):
    1. format PE console
    2. include 'win32axp.inc'
    3. .data
    4. massiv dw 1
    5.        dw 999999 dup 0
    6.  
    7. len dd 1
    8. N dd ?
    9. ten dd 10
    10. inp db 'Input N:',0Dh,0Ah,0;10
    11. d db '%d',0
    12. d04 db '%04d',0
    13. stroka db 1000000 dup 0
    14. hout dd ?
    15. ns dd ?
    16. vvod db 11 dup 0
    17. faiil db 'Program fails',0;13
    18. dllka db 'msvcrt.dll',0
    19. name1 db 'atoi',0
    20. name2 db 'printf',0
    21. atoi dd ?
    22. printf dd ?
    23. .code
    24. fuck:
    25. invoke LoadLibrary,dllka
    26. push eax
    27. invoke GetProcAddress,eax,name1
    28. mov [atoi],eax
    29. pop eax
    30. invoke GetProcAddress,eax,name1
    31. mov [printf],eax
    32. invoke GetStdHandle,STD_OUTPUT_HANDLE
    33. mov [hout],eax
    34. invoke WriteConsole,eax,inp,10,ns,0
    35. invoke GetStdHandle,STD_INPUT_HANDLE
    36. invoke ReadConsole,eax,vvod,10,ns,0
    37. cinvoke atoi,vvod
    38. mov [N],eax
    39.  
    40. xor esi,esi
    41. xor edi,edi
    42. xor ebx,ebx
    43. inc esi
    44.  
    45. factorial:
    46.  cmp esi,[N]
    47.  ja nol
    48.  xor edi,edi
    49. cyclo:
    50.  cmp edi,[len]
    51.  jb testcn
    52. xxx:
    53.  movzx eax,[massiv+edi*2]
    54.  imul eax,esi
    55.  add eax,ebx
    56.  xor edx,edx
    57.  mov ecx,2710h
    58.  div ecx
    59.  mov [massiv+edi*2],dx
    60.  mov eax,ebx
    61.  xor edx,edx
    62.  div ecx
    63.  mov ebx,eax
    64.  inc edi
    65.  jmp cyclo
    66.  
    67. testcn:
    68.  test ebx,ebx
    69.  jnz xxx
    70.  mov [len],edi
    71.  inc esi
    72.  jmp factorial
    73.  
    74. nol:
    75.  mov eax,[len]
    76.  dec eax
    77.  mov edi,eax
    78.  movzx ecx,[massiv+eax*2]
    79.  cinvoke printf,d,ecx
    80.  
    81. cykl:
    82.  dec edi
    83.  test edi,edi
    84.  jz quit
    85.  movzx eax,[massiv+edi*2]
    86.  cinvoke printf,d04,eax
    87.  jmp cykl
    88. jmp quit
    89.  
    90. retry:invoke WriteConsole,[hout],faiil,13,ns,0
    91. quit:invoke ExitProcess,0
    92. .end fuck
    Разница только в том, что я некоторые переменные сразу распихнул по регистрам: i в edi, l в esi, cn в ebx
    Всё компилится, но при работе вызывает исключение во время cn%10000
    Подскажите, пожалуйста, что я делаю не так?
     
  2. Adrax

    Adrax Алексей

    Публикаций:
    0
    Регистрация:
    14 окт 2006
    Сообщения:
    135
    Адрес:
    г. Курск
    Спасибо Great!
    Я косячил с сишными функциями
     
  3. Adrax

    Adrax Алексей

    Публикаций:
    0
    Регистрация:
    14 окт 2006
    Сообщения:
    135
    Адрес:
    г. Курск
    Уважаемые программисты! Прошу прощения, что вновь возвращаюсь к этой избитой теме, но возник ещё ряд вопросов:
    Код, данный мне Nouzui, я переложил на ассемблер, получилось следующее (файл в аттаче) - всё работает, всё отлично

    Теперь мне захотелось использовать функции gmp, тем более, что попался мне на глаза исходничек на ПХП, факториал вычисляющий:
    Код (Text):
    1. <?php
    2.  
    3. function fact ($x) {
    4.  
    5.     if ($x <= 1)
    6.  
    7.   return 1;
    8.  
    9.     else
    10.  
    11.   return gmp_mul ($x, fact ($x-1));
    12.  
    13. }
    14.  
    15.  
    16.  
    17. print gmp_strval (fact (1000)) . "\n";
    18.  
    19. ?>
    Скачал я GMP, распаковал и к сишным инклудам закинул (а юзаю я MS VC++ 2003). Сочинил следующее:
    Код (Text):
    1. #include <stdio.h>
    2. #include <gmp\gmpxx.h>
    3. int N;
    4. int fact(int x)
    5. {
    6. if (x <= 1)
    7. return 1;
    8. else return gmp_mul(x, fact(x-1));
    9. }
    10.  
    11. void main()
    12. {
    13. scanf("%d",&N);
    14. printf("%s\n",gmp_strval(fact(N)));
    15. }
    Не компилится даже, зараза! Я попробовал найти gmp_mul в тамошних хедерах - нету там такой!
    Подскажите, как использовать gmp'шные функции в сишном коде?
     
  4. Pahan

    Pahan New Member

    Публикаций:
    0
    Регистрация:
    10 янв 2009
    Сообщения:
    55
    ECk
    Можно для совсем чайников как сделать так, чтобы можно было вызвывать функции GMP из Delphi
     
  5. s_d_f

    s_d_f New Member

    Публикаций:
    0
    Регистрация:
    15 май 2008
    Сообщения:
    342
    У меня есть программа вместе с исходниками, у меня за 3 минуту факториал 50000 вычислила, более 200000 десятичных знаков и все точно. Толька там с управлением нужно разобраться. Нужно будет сначала и систему исчисления и разрядность указать. http://www.asm1a.narod.ru/calc.rar

    Функция вычисления факториала основана на алгоритме короткого умножения т. е. будь у вас хоть террабайт памяти на супер компе факториал более 2^32 вычисляться не будет

    Код (Text):
    1. Factorial proc uses edi esi ebx n:DWORD
    2.     mov ecx,RegSize    ;в ecx - количество DWORD`ов в числе
    3.     mov edi,R1            ;edi - указатель на число
    4.     xor eax,eax
    5.     rep stosd
    6.     mov esi,n
    7.     mov dword ptr [edi-4],esi
    8.     sub edi,8
    9.     mov ebp,1
    10.     dec esi
    11.     .repeat
    12.         mov ecx,ebp
    13.         xor ebx,ebx
    14.         .repeat
    15.             mov eax,dword ptr [edi+4*ecx]
    16.             mul esi
    17.             add eax,ebx
    18.             adc edx,0
    19.             mov ebx,edx
    20.             mov dword ptr [edi+4*ecx],eax
    21.         .untilcxz
    22.         .if !ZERO?
    23.             mov dword ptr [edi],edx
    24.             .if dword ptr [edi+4*ebp]
    25.                 inc ebp
    26.             .endif
    27.             sub edi,4
    28.         .endif
    29.         dec esi
    30.     .until ZERO?
    31.     pop ebx
    32.     pop edi
    33.     pop esi
    34.     pop ebp
    35.     retn 4
    36. Factorial endp
     
  6. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.792
    Программа на основе кода s_d_f плюс перевод числа в строку считает до 67! - строка из 96 цифр, может быть кому нибудь сгодится
    Код (Text):
    1. .586p
    2. .model flat
    3. .code
    4. include windows.inc
    5. includelib user32.lib
    6. extern _imp__MessageBoxA@16:dword
    7. n equ 67
    8. ;19!=121645100408832000
    9. ;20!=2432902008176640000
    10. ;21!=51090942171709440000
    11. ;22!=1124000727777607680000
    12. ;23!=25852016738884976640000
    13. ;24!=620448401733239439360000
    14. ;25!=15511210043330985984000000
    15. ;26!=403291461126605635584000000
    16. ;27!=10888869450418352160768000000
    17. ;28!=304888344611713860501504000000
    18. ;29!=8841761993739701954543616000000
    19. ;30!=2,6525285981219105863630848e+32
    20. ;31!=8,22283865417792281772556288e+33
    21. ;32!=2,6313083693369353016721801216e+35
    22. ;33!=8,68331761881188649551819440128e+36
    23. ;34!=2,9523279903960414084761860964352e+38
    24. ;35!=1,0333147966386144929666651337523e+40
    25. ;36!=3,7199332678990121746799944815084e+41
    26. ;37!=1,3763753091226345046315979581581e+43
    27. ;38!=5,2302261746660111176000722410007e+44
    28. ;39!=2,0397882081197443358640281739903e+46
    29. ;40!=8,1591528324789773434561126959612e+47
    30. ;41!=3,3452526613163807108170062053441e+49
    31. ;42!=1,4050061177528798985431426062445e+51
    32. ;43!=6,0415263063373835637355132068514e+52
    33. ;44!=2,6582715747884487680436258110146e+54
    34. ;45!=1,1962222086548019456196316149566e+56
    35. ;46!=5,5026221598120889498503054288003e+57
    36. ;47!=2,5862324151116818064296435515361e+59
    37. ;48!=1,2413915592536072670862289047373e+61
    38. ;49!=6,082818640342675608722521633213e+62
    39. ;50!=3,0414093201713378043612608166065e+64
    40. ;51!=1,5511187532873822802242430164693e+66
    41. ;52!=8,0658175170943878571660636856404e+67
    42. ;53!=4,2748832840600255642980137533894e+69
    43. ;54!=2,3084369733924138047209274268303e+71
    44. ;55!=1,2696403353658275925965100847567e+73
    45. ;56!=7,1099858780486345185404564746372e+74
    46. ;57!=4,0526919504877216755680601905432e+76
    47. ;58!=2,3505613312828785718294749105151e+78
    48. ;59!=1,3868311854568983573793901972039e+80
    49. ;60!=8,3209871127413901442763411832234e+81
    50. ;61!=5,0758021387722479880085681217663e+83
    51. ;62!=3,1469973260387937525653122354951e+85
    52. ;63!=1,9826083154044400641161467083619e+87
    53. ;64!=1,2688693218588416410343338933516e+89
    54. ;65!=8,2476505920824706667231703067855e+90
    55. ;66!=5,4434493907744306400372924024784e+92
    56. ;67!=3,6471110918188685288249859096605e+94
    57. ;68!=2,4800355424368305996009904185692e+96
    58. start:  mov edi,offset result+len;edi - указатель на последний байт результата
    59.     mov esi,n
    60.     mov dword ptr [edi-4],esi
    61.     sub edi,8
    62.     mov ebp,1
    63.     dec esi
    64. @1: mov ecx,ebp
    65.     xor ebx,ebx
    66. @@: mov eax,dword ptr [edi+4*ecx]
    67.     mul esi
    68.     add eax,ebx
    69.     adc edx,0
    70.     mov ebx,edx
    71.     mov dword ptr [edi+4*ecx],eax
    72.     loop @b;.untilcxz
    73.     je @2;  .if !ZERO?
    74.     mov dword ptr [edi],edx
    75.     cmp dword ptr [edi+4*ebp],0
    76.     je @f
    77.     inc ebp
    78. @@: sub edi,4
    79. @2: dec esi
    80.     jne @1;.until ZERO?
    81. ;little-endian --> big-endian
    82.     mov edi,offset result
    83.     mov cl,len/4;10
    84. @@: mov eax,[edi]
    85.     bswap eax
    86.     stosd
    87.     loop @b
    88. ; перевожу hex->dec
    89.     mov esi,offset mesbox_text+94        
    90. @3: mov cl,len;длина в байтах значения делимого
    91.     mov edi,offset result
    92.     xor eax,eax
    93. @@: mov al,[edi];делимое
    94.     div ten
    95.     stosb
    96.     loop @b
    97.     or [esi],ah;остаток
    98.     dec esi
    99.     cmp dword ptr [edi-4],0;cmp result+36,0
    100.     jne @3
    101.     cmp dword ptr [edi-8],0;cmp result+32,0
    102.     jne @3
    103. ; вывожу на экран
    104.     inc esi
    105.     push MB_OK + MB_ICONASTERISK
    106.     push offset mesbox_title  
    107.     push esi;offset mesbox_text с поправкой на "ведущие" нули
    108.     push 0
    109.     call _imp__MessageBoxA@16
    110.     ret;выходим из программы
    111. result  db 40 dup (0)
    112. len = $-result
    113. ten db 10
    114. mesbox_text db 95 dup ('0'),0
    115. mesbox_title db 'Факториал',0
    116. end start
     
  7. AndreyMust19

    AndreyMust19 New Member

    Публикаций:
    0
    Регистрация:
    20 окт 2008
    Сообщения:
    714
    Быстрее будет записать факториал всех 20000 чисел в массив и читать уже готовые значение. Нужно взять любой исходник, записать результаты факториалов в файл (в понятном программе формате). Это лучше, чем сидеть и мучаться "как же это быстрее посчитать" и потом писать совершенно нечитаемые программы.
     
  8. at0s

    at0s New Member

    Публикаций:
    0
    Регистрация:
    13 июл 2009
    Сообщения:
    91
    я как-то давно написал такую программу на fortrane
    попробуй представить себе 96-и битное число записанное в регистрах eax, ebx, ecx ну или где-то в памяти
    и умножь его на 3.
    Чтоб понять алгоритм, на бумажке перемножь 12 34 56 на 3, считая группу регистром, обрати внимание на
    переносы. Этой логикой можно считать любые факториалы, и единственным ограничением будет доступная
    память компьютера
     
  9. persicum

    persicum New Member

    Публикаций:
    0
    Регистрация:
    2 фев 2007
    Сообщения:
    947
    Имхо фигней стадаете, господа.
    1000000! вычисляется за пол секунды и требует 8 мешков рама,
    ну а 10000000! соответственно за 5 сек и требует 80 мешков рама.

    Алгоритм слияния по степеням двойки. Полезно предварительно развести массив нулями в двое. Пример для 9 факториал.

    2030405060708090
    6000020024002700
    0210000042030000
    0882630000000000
     
  10. persicum

    persicum New Member

    Публикаций:
    0
    Регистрация:
    2 фев 2007
    Сообщения:
    947
    Как видно, просматривать массив нужно логарифмическое число раз.
    Надеюсь, объяснять каждый шаг не нужно... А для тех кто в танке скажу.
    Шаг выполняется путем циклической свертки.

    Вощщем та, я бы мог это зарелизить, но какой прок иметь например 90 или 900 мешков случайных цыфирь? Зато такой огроменный факториал нужен например для кодов РидаСоломона, и там я его сделал.
     
  11. persicum

    persicum New Member

    Публикаций:
    0
    Регистрация:
    2 фев 2007
    Сообщения:
    947
    Малешка недооценил я вычислительные сложности и требования по памяти и времени. =(((
    Вообщем, ближе к делу оказалось, что

    1000000! вычисляется за 30 секунд,
    10000000! вычисляется за 10 минут на c2d.

    А памяти жрет 1.6G =)))

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

    Посколько такие факториалы это вовсе не бегинерс, а скорее крутос, то брать мою прогу можно в проджектс.
     
  12. t00x

    t00x New Member

    Публикаций:
    0
    Регистрация:
    15 фев 2007
    Сообщения:
    1.921
    persicum
    ну, а где функция подсчета (или хотя бы приблизительно) количества нулей в конце каждого факториала? ;).