Liczba Wampiryczna jest to taka liczba a, która ma parzystą liczbę cyfr. Na podstawie cyfr liczby a można utworzyć dwie liczby x i y, długości równej połowie długości liczby a. Pomnożone przez siebie dają a. Wartości x i y są nazywane kłami (ang. fang).
Najmniejszą liczbą Wampiryczną jest 1260, ponieważ z cyfr tej liczby można utworzyć x = 21 oraz y = 60. Iloczyn x i y jest to liczba 1260. Liczby Wampiryczne mogą mieć więcej niż jeden zestaw x i y. Przykładem takiej liczby jest 125460. Z jej cyfr można utworzyć pary (x, y) = {(204, 615), (246, 510)}.
Poniżej zostały przedstawione dwa sposoby na sprawdzenie czy liczba a jest liczbą Wampiryczną. Pierwsze z nich ma podejście kombinatoryczne, które okazują się mało efektywne dla coraz to większych liczb. Drugie podejście wykorzystuje zależności matematyczne, aby zoptymalizować proces sprawdzania.
Z definicji wynika, że liczby x i y mają te same cyfry co a, dlatego można sprawdzić wszystkie kombinacje ułożenia cyfr liczby a. Dla każdej kombinacji następnie należy pomnożyć dwie części liczby a po rozsunięciu ich w połowie. Metoda ta nie jest niestety efektywna, ponieważ w trakcie poszukiwania rozwiązania należy przejrzeć n! przypadków.
Zgodnie ze strategią funkcja czyWampiryczna() ma wstępnie sprawdzić czy liczba będzie miała jakiekolwiek szanse.
(2.) Oblicz długość liczby a. (3.) Dalsze sprawdzanie przeprowadź tylko, gdy długość jest parzysta. (Jeśli jest nieparzysta to (14.).) Utwórz (4. - 9.) listę cyfr liczby a. (10.) Rozpocznij poszukiwania jakiejkolwiek prawidłowej kombinacji i zapamiętaj wynik. (11.) Zdealokuj listę i (12.) zwróć wynik.
Poszukiwanie kombinacji przeprowadza funkcja szukajKombinacji(), która potrzebuje a - sprawdzaną liczbę, data - listę cyfr, dl - długość liczby oraz i, który element jest aktualnie ustalany.
(2.) Jeśli program uzyskał pełną kombinacje to (3.) sprawdź czy obie liczby mają długość równej połowie długości liczby a. Jeśli nie to (4.) zwróć, że kombinacja jest nieprawidłowa. W przeciwnym wypadku (5. - 6.) pobierz obie liczby z kombinacji i (7.) zwróć wynik porównania pomnożenia pobranych wartości oraz liczby a.
(9.) W przypadku, gdy trwa poszukiwanie kombinacji (nie wszystkie pola zostały ustalone) to: (10. - 15.) ustal kolejny element i ponownie (12.) wywołaj szukajKombinacji() z nowymi danymi. Tylko jeśli funkcja zwróci prawdę to (13.) też zwróć prawdę. Jeśli zostały sprawdzone wszystkie kombinacje i nie została zwrócona prawda to (17.) zwróć fałsz.
W trakcie pisania powyższego rozwiązania zostały użyte dwie funkcje pomocnicze. Pierwsza z nich zamienDane() zamieniała dwie wartości na liście.
Druga funkcja pomocnicza pomaga odczytać dl kolejnych wartości z podanej listy data począwszy od pozycji poz:
Istnieje możliwość optymalizacji poprzedniego rozwiązania. Po pierwsze nie ma potrzeby sprawdzania wszystkich kombinacji. Można wyznaczyć wszystkie dzielniki liczby a, które mają długość równą połowie długości a. Czyli dla liczby długości 2k należy znaleźć dzielniki w przedziale [10k - 1, 10k).
Ponadto w takim przypadku wystarczy policzyć ile jest każdej cyfry w liczbie a oraz w znalezionych liczbach x i y. To pozwala zaoszczędzić pamięć poprzez uniezależnienie długości listy od długości liczby a. (W podejściu kombinatorycznym wykorzystanie pamięci rosło liniowo).
(2.) Policz długość liczby a i (3.) sprawdź czy ma parzystą liczbę cyfr. (4.) W celu uproszczenia zapisu oblicz połowę liczby dl. (5.) Utwórz listę cyfr liczby a i (6. - 7.) wyznacz przedział w którym będą szukane dzielniki liczby a. (8.) Sprawdź czy zakres nie rozpoczyna się od zera. Jeśli tak to (9.) większy dolny zakres.
Następnie (10.) dla każdej liczby z zakresu (11.) sprawdź czy jest ona dzielnikiem a oraz czy jej długość to połowa długości a. Jeśli tak to (12.) Wyznacz liczbę, która jest złożeniem znalezionych wartości x = dzielnik oraz y = a / dzielnik. (13.) Policz ile jest każdej z cyfr. (14.) Przypuść, że jest tyle samo każdej z cyfr w a co w znalezionym rozkładzie. (15. - 16.) Porównaj ilości cyfr a i rozw. (17.) Dealokuj pamięć potrzebną pod cyfry znalezionego rozkładu. (18.) Jeśli wynik jest prawdziwy to (19.) zdealokuj pamięć i (20.) zwróć wynik (tj. prawdę). Jeśli jednak po przejrzeniu całego zakresu nie znajdzie się ani jedna para to (24.) usuń informacje o liczbie a i (26.) zwróć fałsz.
W celu uproszczenia kodu napisana została funkcja listaCyfr(), która dla danej liczby a zwraca listę, która zawiera ile sztuk każdej cyfry ma liczba a.
Ze względu na wykorzystanie w dwóch miejscach obliczanie długości liczby też zostało zapisane jako oddzielna funkcja dlugoscLiczby():
Obydwie metody prawidłowo wypisują kolejne liczby wampiryczne. Warto zwrócić uwagę, że drugie rozwiązanie działa ponad 10 razy szybciej od pierwszego. Pierwsze rozwiązanie kombinatoryczne wykonuje dla sprawdzanej liczby n! sprawdzeń utworzonej kombinacji. Druga funkcja działa szybciej, ponieważ sprawdza jedynie określony zakres i nie zgaduje dzielników a, a je wyznacza. Nawet zakładając, że co druga wartość zakresu to dzielnik to zostanie wykonane maksymalnie wykonane porównań.
Długość liczby | Metoda I (a) | Metoda II (b) | a / b |
---|---|---|---|
2 | 2 | 4 | 0,5 |
4 | 24 | 21 | 1,142857143 |
6 | 720 | 143 | 5,034965035 |
8 | 40320 | 1061 | 38,00188501 |
10 | 3628800 | 8363 | 433,9112759 |
W tabeli dla Metody I zostały wypisane ile zostało znalezionych kombinacji do sprawdzenia. W metodzie II odpowiednikiem jest ilość pierwszych dzielników liczby a.
Jak widać Metoda II okazuje się początkowo mniej efektywna niż Metoda I. Jednak bardzo szybko się okazuje, że Metoda II może być nawet 433 razy szybsza dla liczb 10 cyfrowych!
W celu wypisania wszystkich liczb Wampirycznych z przedziału [1, 200000] wystarczy poniższa funkcja main().
Napisz program, który wczyta od użytkownika liczbę a, a następnie wypisze pary liczb, które świadczą o tym, że a jest wampiryczna. Jeśli liczba a nie jest wampiryczna to należy wypisać na ekran stosowny komunikat.
Przykładowo dla liczby 125460 zostanie wypisane: