Biorąc pod uwagę liczbę całkowitą n
, oblicz zestaw n
losowych unikatowych liczb całkowitych w zakresie 1..n^2
(włącznie) tak, aby suma tego zbioru była równan^2
W tym przypadku losowy oznacza równomiernie losowy między prawidłowymi wyjściami. Każde prawidłowe wyjście dla danej n
musi mieć jednolitą szansę na wygenerowanie.
Na przykład, n=3
powinien mieć szansę 1/3 każdego wyprowadzania 6, 1, 2
, 3, 5, 1
albo 4, 3, 2
. Ponieważ jest to zestaw, kolejność jest nieistotna, 4, 3, 2
jest identyczna z3, 2, 4
Punktacja
Zwycięzcą jest program, który może obliczyć najwyższy n
w czasie krótszym niż 60 sekund.
Uwaga: Aby zapobiec możliwemu częściowemu kodowaniu na sztywno, wszystkie wpisy muszą mieć mniej niż 4000 bajtów
Testowanie
Cały kod będzie uruchamiany na moim lokalnym komputerze z systemem Windows 10 (Razer Blade 15, 16 GB RAM, Intel i7-8750H 6 rdzeni, 4.1GHz, GTX 1060 na wypadek, gdybyś chciał nadużyć GPU), więc podaj szczegółowe instrukcje, aby uruchomić swój kod na moja maszyna.
Na żądanie wpisy mogą być uruchamiane za pośrednictwem Debiana na WSL lub na maszynie wirtualnej Xubuntu (obie na tej samej maszynie, jak powyżej)
Zgłoszenia będą przeprowadzane 50 razy z rzędu, końcowy wynik będzie średnią z wszystkich 50 wyników.
źródło
Odpowiedzi:
Rdza , n ≈ 1400
Jak biegać
Kompiluj
cargo build --release
i uruchamiaj ztarget/release/arbitrary-randomness n
.Ten program działa najszybciej z dużą ilością pamięci (o ile oczywiście nie zamienia). Możesz dostosować zużycie pamięci, edytując
MAX_BYTES
stałą, obecnie ustawioną na 8 GiB.Jak to działa
Zbiór jest konstruowany na podstawie sekwencji decyzji binarnych (każda liczba znajduje się wewnątrz lub na zewnątrz zestawu), z których każde prawdopodobieństwo jest obliczane kombinatorycznie poprzez zliczenie liczby możliwych zestawów możliwych do zbudowania po każdym wyborze za pomocą programowania dynamicznego.
Wykorzystanie pamięci dla dużych n jest zmniejszone przez zastosowanie wersji tej dwumianowej strategii partycjonowania .
Cargo.toml
src/main.rs
Wypróbuj online!
(Uwaga: wersja TIO ma kilka modyfikacji. Po pierwsze, limit pamięci jest zredukowany do 1 GiB. Po drugie, ponieważ TIO nie pozwala na napisanie a
Cargo.toml
i zależy od zewnętrznych skrzynek, na przykładrand
, zamiast tego ściągnąłemdrand48
z biblioteki C za pomocą FFI. Nie zadałem sobie trudu, aby go zaszczepić, więc wersja TIO będzie generować taki sam wynik przy każdym uruchomieniu. Nie używaj wersji TIO do oficjalnych testów porównawczych.)źródło
ln_add_exp
, sprawdzając, czy różnica bezwzględna jest większa niż około 15 lub więcej, co może być szybsze, jeśli takich dodatków jest dużo.ln_add_exp
połączenia wymagają porównywalnych danych wejściowych.Java 7+, n = 50 w ~ 30 sekund na TIO
Niegolfowana wersja mojej odpowiedzi na kodową wersję tego wyzwania na razie, z jedną drobną zmianą:
java.util.Random#nextInt(limit)
jest używana zamiast liczby(int)(Math.random()*limit)
całkowitej w zakresie[0, n)
, ponieważ jest ona około dwa razy szybsza .Wypróbuj online.
Wyjaśnienie:
Zastosowane podejście:
Kod jest podzielony na dwie części:
n
losowych liczb całkowitych, które sumują sięn squared
.Krok 1 składa się z następujących podetapów:
1) Wygeneruj tablicę
n-1
liczb losowych liczb całkowitych w zakresie[0, n squared)
. I dodaj0
in squared
do tej listy. Odbywa się to wO(n+1)
wydajności.2) Następnie posortuje tablicę z wbudowanym
java.util.Arrays.sort(int[])
, Jest to wykonywane wO(n*log(n))
wydajności, jak stwierdzono w dokumentach:3) Oblicz różnicę między każdą parą. Ta wynikowa lista różnic będzie zawierać
n
liczby całkowite, które sumują sięn squared
. Odbywa się to wO(n)
wydajności.Oto przykład:
Te trzy powyższe kroki są całkiem dobre pod względem wydajności, w przeciwieństwie do kroku 2 i pętli wokół całości, która jest podstawową brutalną siłą. Krok 2 dzieli się na następujące podetapy:
1) Lista różnic jest już zapisana w pliku
java.util.Set
. Sprawdzi, czy rozmiar tego zestawu jest równyn
. Jeśli tak, oznacza to, że wszystkie wygenerowane przez nas losowe wartości są unikalne.2) I sprawdzi również, czy nie zawiera go
0
w zestawie, ponieważ wyzwanie prosi o losowe wartości w zakresie[1, X]
, gdzieX
jestn squared
minus suma[1, ..., n-1]
, jak stwierdził @Skidsdev w komentarzu poniżej.Jeśli jedna z dwóch powyższych opcji (nie wszystkie wartości są unikalne lub zero jest obecne), wygeneruje nową tablicę i ustawi ponownie, resetując do kroku 1. Trwa to do momentu uzyskania wyniku. Z tego powodu czas może się nieco różnić. Widziałem, jak kończy się za 3 sekundy na TIO
n=50
, ale także za 55 sekundn=50
.Dowód jednolitości:
Nie jestem do końca pewien, jak to udowodnić. Na
java.util.Random#nextInt
pewno jest jednolity, jak opisano w dokumentach:Różnice między tymi (posortowanymi) wartościami losowymi same w sobie oczywiście nie są jednolite, ale zestawy jako całość są jednolite. Ponownie, nie jestem pewien, jak to udowodnić matematycznie, ale tutaj jest skrypt, który umieści
10,000
wygenerowane zestawy (dlan=10
) na mapie z licznikiem , gdzie większość zestawów jest unikalna; niektóre powtórzyły się dwukrotnie; a maksymalne powtarzające się wystąpienie zwykle mieści się w zakresie[4,8]
.Instrukcje Instalacji:
Ponieważ Java jest dość dobrze znanym językiem z dużą ilością dostępnych informacji na temat tworzenia i uruchamiania kodu Java, powiem to krótko.
Wszystkie narzędzia użyte w moim kodzie są dostępne w Javie 7 (być może nawet już w Javie 5 lub 6, ale na wszelki wypadek użyjmy 7). Jestem pewien, że Java 7 jest już zarchiwizowana, więc sugeruję pobranie Java 8, aby uruchomić mój kod.
Myśli dotyczące ulepszeń:
Chciałbym znaleźć ulepszenie w zakresie sprawdzania zer i sprawdzania, czy wszystkie wartości są unikalne. Mógłbym to sprawdzić
0
wcześniej, upewniając się, że losowa wartość, którą dodajemy do tablicy, już jej nie ma, ale oznaczałoby to kilka rzeczy: tablica powinna być,ArrayList
więc możemy użyć wbudowanej metody.contains
; pętla while powinna być dodawana, dopóki nie znajdziemy losowej wartości, która nie znajduje się jeszcze na liście. Ponieważ sprawdzanie zera jest teraz wykonywane.contains(0)
na zestawie (który jest sprawdzany tylko raz), najprawdopodobniej lepiej jest sprawdzić wydajność w tym punkcie, w porównaniu do dodawania pętli z.contains
listą, która będzie sprawdzana przynajmniejn
raz , ale najprawdopodobniej więcej.Jeśli chodzi o kontrolę unikatowości, mamy tylko
n
liczbę losowych liczb całkowitych, które sumują sięn squared
po kroku 1 programu, więc tylko wtedy możemy sprawdzić, czy wszystkie są unikalne. Może być możliwe utrzymanie sortowalnej listy zamiast tablicy i sprawdzenie różnic między nimi, ale poważnie wątpię, że poprawi to wydajność, niż tylko umieszczenie ich wSet
i sprawdzenie, czy rozmiar tego zestawu jestn
jeden.źródło
n^2 - sum(1..n-1)
na przykład dlan=5
największej poprawnej liczby5^2 - sum(1, 2, 3, 4) == 25 - 10 == 15
n
, prawda? W takim przypadku możesz dodać0
do zestawu, a następnie sprawdzić, czy rozmiar jest (teraz) większy niżn
. Może się to zdarzyć tylko wtedy, gdy wszystkie różnice są niezerowe i wyraźne.HashSet.contains
jest w większości przypadków bliskoO(1)
, aw najgorszym przypadku jestO(n)
w Javie 7 iO(log n)
Javie 8+ (poprawiono po zastąpieniu łączenia łańcuchem z wykrywaniem kolizji). Jeśli wolno mi zwrócić zestaw z dodanym0
do sprawdzenia, to rzeczywiście jest nieco lepiej dla wydajności, ale jeśli muszę zadzwonić doset.remove(0);
wewnątrz, jestem pewien, że wydajność jest nieco taka sama.Matematyka n = 11
źródło