Jaki byłby najbardziej optymalny algorytm (pod względem wydajności) do obliczenia liczby dzielników podanej liczby?
Byłoby wspaniale, gdybyś mógł podać pseudokod lub link do jakiegoś przykładu.
EDYCJA: Wszystkie odpowiedzi były bardzo pomocne, dziękuję. Wdrażam Sieve of Atkin, a potem zamierzam użyć czegoś podobnego do tego, co wskazał Jonathan Leffler. Link zamieszczony przez Justina Bozoniera zawiera dalsze informacje o tym, czego chciałem.
Odpowiedzi:
Dmitriy ma rację, że chcesz, aby Sieve of Atkin wygenerowała pierwszą listę, ale nie sądzę, aby to załatwiło całą sprawę. Teraz, gdy masz już listę liczb pierwszych, musisz zobaczyć, ile z tych liczb pierwszych działa jako dzielnik (i jak często).
Oto python dla algo.Szukaj tutaj i wyszukaj „Temat: matematyka - algorytm potrzeb dzielników”. Po prostu policz liczbę pozycji na liście, zamiast je zwracać.Oto Dr. Math, który wyjaśnia, co dokładnie musisz zrobić matematycznie.
Zasadniczo sprowadza się to do tego, że jeśli twoja liczba
n
to:n = a^x * b^y * c^z
(gdzie a, b i c są pierwszymi dzielnikami n, a x, y i z to liczba powtórzeń dzielnika), to całkowita liczba wszystkich dzielników wynosi:
(x + 1) * (y + 1) * (z + 1)
.Edycja: BTW, aby znaleźć a, b, c, itd., Będziesz chciał zrobić coś, co sprowadza się do chciwego algo, jeśli dobrze to rozumiem. Zacznij od największego dzielnika pierwszego i pomnóż go przez siebie, aż dalsze pomnożenie przekroczy liczbę n. Następnie przejdź do następnego najniższego współczynnika i razy poprzedniej liczby pierwszej ^ ile razy została pomnożona przez aktualną liczbę pierwszą i kontynuuj mnożenie przez liczbę pierwszą, aż następny przekroczy n ... itd. Śledź, ile razy pomnożyłeś dzielniki razem i zastosuj te liczby do powyższego wzoru.
Nie jestem w 100% pewien co do mojego opisu algo, ale jeśli to nie jest to, to coś podobnego.
źródło
n = (a^x * b^y * c^z)-(x + 1) * (y + 1) * (z + 1)
jest zasadaIstnieje o wiele więcej technik faktorowania niż sito Atkina. Na przykład przypuśćmy, że chcemy podzielić na czynniki 5893. Cóż, jego sqrt to 76,76 ... Teraz spróbujemy zapisać 5893 jako iloczyn kwadratów. Cóż (77 * 77 - 5893) = 36, czyli 6 do kwadratu, więc 5893 = 77 * 77 - 6 * 6 = (77 + 6) (77-6) = 83 * 71. Gdyby to nie zadziałało, sprawdzilibyśmy, czy 78 * 78 - 5893 to idealny kwadrat. I tak dalej. Dzięki tej technice możesz szybko przetestować czynniki w pobliżu pierwiastka kwadratowego z n znacznie szybciej niż testując pojedyncze liczby pierwsze. Jeśli połączysz tę technikę wykluczania dużych liczb pierwszych z sitem, uzyskasz znacznie lepszą metodę faktoryzacji niż w przypadku samego sita.
A to tylko jedna z wielu opracowanych technik. To jest dość proste. Zajęłoby ci dużo czasu, aby nauczyć się, powiedzmy, wystarczającej teorii liczb, aby zrozumieć techniki faktoryzacji oparte na krzywych eliptycznych. (Wiem, że istnieją. Nie rozumiem ich.)
Dlatego jeśli nie masz do czynienia z małymi liczbami całkowitymi, nie próbowałbym sam rozwiązać tego problemu. Zamiast tego spróbuję znaleźć sposób na użycie czegoś takiego jak biblioteka PARI, która ma już wdrożone wysoce wydajne rozwiązanie. Dzięki temu mogę rozliczyć losową 40-cyfrową liczbę, taką jak 124321342332143213122323434312213424231341 w około 0,05 sekundy. (Jego faktoryzacja, na wypadek, gdybyś się zastanawiała, wynosi 29 * 439 * 1321 * 157907 * 284749 * 33843676813 * 4857795469949. Jestem całkiem pewien, że nie doszedł do tego za pomocą sita Atkina ...)
źródło
@Yasky
Twoja funkcja dzielników ma błąd polegający na tym, że nie działa poprawnie dla idealnych kwadratów.
Próbować:
źródło
Nie zgadzam się, że sito Atkina jest drogą do zrobienia, ponieważ sprawdzenie każdej liczby w [1, n] może zająć więcej czasu niż zmniejszenie liczby o podziały.
Oto kod, który, choć nieco bardziej skomplikowany, jest generalnie znacznie szybszy:
ps To działający kod w Pythonie, który rozwiązuje ten problem.
źródło
Oto prosty algorytm O (sqrt (n)). Użyłem tego do rozwiązania projektu euler
źródło
To interesujące pytanie jest o wiele trudniejsze, niż się wydaje, i nie ma na nie odpowiedzi. Pytanie można rozłożyć na 2 bardzo różne pytania.
1 mając N, znajdź listę L czynników pierwszych N.
2 biorąc pod uwagę L, oblicz liczbę unikalnych kombinacji
Wszystkie odpowiedzi, które do tej pory widzę, odnoszą się do nr 1 i nie wspominają, że nie da się tego rozwiązać w przypadku ogromnych liczb. Dla średniej wielkości N, nawet 64-bitowych, jest to łatwe; dla ogromnego N problem faktoringu może trwać „wiecznie”. Od tego zależy szyfrowanie klucza publicznego.
Pytanie nr 2 wymaga dalszej dyskusji. Jeśli L zawiera tylko unikalne liczby, jest to proste obliczenie przy użyciu wzoru kombinacji do wyboru k obiektów z n elementów. W rzeczywistości należy zsumować wyniki zastosowania wzoru, zmieniając k od 1 do sizeof (L). Jednak L zwykle zawiera wiele wystąpień wielu liczb pierwszych. Na przykład L = {2,2,2,3,3,5} to faktoryzacja N = 360. Teraz ten problem jest dość trudny!
Powtarzając # 2, dany zbiór C zawiera k elementów, tak że element a ma „duplikaty, a element b ma duplikaty b” itd. Ile jest unikalnych kombinacji elementów od 1 do k-1? Na przykład {2}, {2,2}, {2,2,2}, {2,3}, {2,2,3,3} muszą wystąpić raz i tylko raz, jeśli L = {2,2 , 2,3,3,5}. Każda taka unikalna pod-kolekcja jest niepowtarzalnym dzielnikiem N przez pomnożenie elementów w pod-kolekcji.
źródło
p_i
jest czynnikiem pierwszym liczby ok_i
krotności, całkowita liczba dzielników tej liczby wynosi(k_1+1)*(k_2+1)*...*(k_n+1)
. Myślę, że już to wiesz, ale zapisuję to na korzyść przypadkowego czytelnika.Odpowiedź na twoje pytanie zależy w dużej mierze od wielkości liczby całkowitej. Metody dla małych liczb, np. Mniejszych niż 100 bitów i dla liczb ~ 1000 bitów (takich jak stosowane w kryptografii) są zupełnie inne.
ogólny przegląd: http://en.wikipedia.org/wiki/Divisor_function
wartości małych
n
i przydatnych odniesień: A000005: d (n) (zwane również tau (n) lub sigma_0 (n)), liczba dzielników n.przykład ze świata rzeczywistego: faktoryzacja liczb całkowitych
źródło
TYLKO jedna linia
Bardzo uważnie przemyślałem twoje pytanie i próbowałem napisać bardzo wydajny i wydajny fragment kodu. Aby wypisać na ekranie wszystkie dzielniki danej liczby, potrzebujemy tylko jednej linii kodu! (użyj opcji -std = c99 podczas kompilacji przez gcc)
aby znaleźć liczby dzielników, możesz użyć następującej bardzo szybkiej funkcji (działa poprawnie dla wszystkich liczb całkowitych z wyjątkiem 1 i 2)
lub jeśli traktujesz podaną liczbę jako dzielnik (działa poprawnie dla wszystkich liczb całkowitych z wyjątkiem 1 i 2)
UWAGA: dwie powyższe funkcje działają poprawnie dla wszystkich dodatnich liczb całkowitych z wyjątkiem liczb 1 i 2, więc działają one dla wszystkich liczb, które są większe niż 2, ale jeśli potrzebujesz pokryć 1 i 2, możesz użyć jednej z następujących funkcji (trochę wolniej)
LUB
małe jest piękne :)
źródło
Sito Atkina jest zoptymalizowaną wersją sita Eratostenesa, które podaje wszystkie liczby pierwsze aż do podanej liczby całkowitej. Powinieneś być w stanie znaleźć to w Google, aby uzyskać więcej szczegółów.
Gdy masz już tę listę, łatwo jest podzielić swoją liczbę przez każdą liczbę pierwszą, aby sprawdzić, czy jest to dokładny dzielnik (tj. Reszta to zero).
Podstawowe kroki obliczania dzielników dla liczby (n) to [to jest pseudokod przekonwertowany z prawdziwego kodu, więc mam nadzieję, że nie wprowadziłem błędów]:
źródło
Możesz spróbować tego. To trochę hakerskie, ale dość szybkie.
źródło
Gdy masz już rozkład na czynniki pierwsze, istnieje sposób na znalezienie liczby dzielników. Dodaj po jednym do każdego wykładnika każdego czynnika, a następnie pomnóż wykładniki razem.
Na przykład: 36 Rozkład na czynniki pierwsze: 2 ^ 2 * 3 ^ 2 Dzielniki: 1, 2, 3, 4, 6, 9, 12, 18, 36 Liczba dzielników: 9
Dodaj po jednym do każdego wykładnika 2 ^ 3 * 3 ^ 3 Pomnóż wykładniki: 3 * 3 = 9
źródło
Zanim zdecydujesz się na rozwiązanie, weź pod uwagę, że podejście Sieve może nie być dobrą odpowiedzią w typowym przypadku.
Jakiś czas temu pojawiło się pierwsze pytanie i zrobiłem test czasu - dla 32-bitowych liczb całkowitych przynajmniej określenie, czy jest to liczba pierwsza, było wolniejsze niż brutalna siła. Istnieją dwa czynniki:
1) Podczas gdy człowiekowi zajmuje się trochę czasu, aby dokonać podziału, jest on bardzo szybki na komputerze - podobnie jak koszt szukania odpowiedzi.
2) Jeśli nie masz tabeli głównej, możesz utworzyć pętlę działającą w całości w pamięci podręcznej L1. To sprawia, że jest szybszy.
źródło
To wydajne rozwiązanie:
źródło
Dzielniki robią coś spektakularnego: całkowicie dzielą. Jeśli chcesz sprawdzić liczbę dzielników dla wielu,
n
to wyraźnie jest zbędne obejmować całe spektrum,1...n
. Nie przeprowadziłem żadnych szczegółowych badań, ale rozwiązałem problem 12 projektu Eulera dotyczący liczb trójkątnych . Moje rozwiązanie dla testu dzielników większych niż 500 działało przez 309504 mikrosekund (~ 0,3 s). Napisałem tę funkcję dzielnika dla rozwiązania.Każdy algorytm ma słaby punkt. Myślałem, że to słabe w porównaniu z liczbami pierwszymi. Ale ponieważ liczby trójkątne nie są drukowane, spełniło swoje zadanie bezbłędnie. Sądząc po moim profilowaniu, myślę, że wyszło całkiem nieźle.
Wesołych Świąt.
źródło
numberOfDivisors
i iterator na 1; powinno to wyeliminować błąd dzielenia przez zeroChcesz Sieve of Atkin, opisaną tutaj: http://en.wikipedia.org/wiki/Sieve_of_Atkin
źródło
metoda liczb pierwszych jest tutaj bardzo jasna. P [] jest listą liczb pierwszych mniejszych lub równych sq = sqrt (n);
źródło
Podręczniki teorii liczb nazywają funkcję liczenia dzielników tau. Pierwszym interesującym faktem jest to, że jest multiplikatywny, tj. τ (ab) = τ (a) τ (b), gdy a i b nie mają wspólnego dzielnika. (Dowód: każda para dzielników a i b daje odrębny dzielnik ab).
Teraz zauważ, że dla pa prim, τ (p ** k) = k + 1 (potęgi p). W ten sposób możesz łatwo obliczyć τ (n) na podstawie jego faktoryzacji.
Jednak faktoryzacja dużych liczb może być powolna (bezpieczeństwo kryptografii RSA zależy od iloczynu dwóch dużych liczb pierwszych, które są trudne do faktoryzacji). To sugeruje ten zoptymalizowany algorytm
źródło
Poniżej znajduje się program w C do znajdowania liczby dzielników podanej liczby.
Złożoność powyższego algorytmu wynosi O (sqrt (n)).
Algorytm ten będzie działał poprawnie dla liczb, które są idealnie kwadratowe, a także dla liczb, które nie są idealnie kwadratowe.
Zauważ, że górny limit pętli jest ustawiony na pierwiastek kwadratowy z liczby, aby algorytm był najbardziej wydajny.
Zauważ, że przechowywanie górnego limitu w oddzielnej zmiennej również oszczędza czas, nie powinieneś wywoływać funkcji sqrt w sekcji warunków pętli for, oszczędza to również czas obliczeniowy.
Zamiast powyższej pętli for możesz również użyć następującej pętli, która jest jeszcze bardziej wydajna, ponieważ eliminuje potrzebę znajdowania pierwiastka kwadratowego z liczby.
źródło
Oto funkcja, którą napisałem. jego najgorsza złożoność czasowa to O (sqrt (n)), natomiast najlepszy czas to O (log (n)). Daje wszystkie pierwsze dzielniki wraz z liczbą ich wystąpień.
źródło
Oto najbardziej podstawowy sposób obliczania dzielników liczb:
źródło
@Kendall
Przetestowałem Twój kod i wprowadziłem kilka ulepszeń, teraz jest jeszcze szybszy. Testowałem również z kodem @ هومن جاویدپور, jest to również szybsze niż jego kod.
źródło
Czy nie jest to tylko kwestia rozłożenia na czynniki liczby - określenia wszystkich czynników liczby? Następnie możesz zdecydować, czy potrzebujesz wszystkich kombinacji jednego lub więcej czynników.
Tak więc jeden możliwy algorytm to:
Następnie od Ciebie zależy połączenie czynników, aby określić pozostałą część odpowiedzi.
źródło
To jest coś, co wymyśliłem na podstawie odpowiedzi Justina. Może wymagać pewnej optymalizacji.
źródło
Myślę, że właśnie tego szukasz, robię dokładnie to, o co prosiłeś. Skopiuj i wklej go w Notatniku Zapisz jako * .bat.Run.Wprowadź numer.Proces pomnóż przez 2 i to jest liczba dzielników.Zrobiłem to celowo, aby szybciej określić dzielniki:
Pls pamiętać, że zmienna CMD obsługuje wartości powyżej 999999999
źródło
myślę, że ten będzie poręczny i precyzyjny
script.pyton
>>>factors=[ x for x in range (1,n+1) if n%x==0] print len(factors)
źródło
Spróbuj czegoś w ten sposób:
źródło
Nie znam NAJBARDZIEJ wydajnej metody, ale zrobiłbym co następuje:
Powinien działać \ o /
Jeśli potrzebujesz, mogę jutro zaprogramować coś w C, aby zademonstrować.
źródło