Zaloguj się, aby obserwować  
UthersonL

Programowanie - pytania, problemy, przykłady programów

1812 postów w tym temacie

Ale mogę, sam wskazywałeś na KISS. Więc teraz pozwól mi wyodrębnić funkcję losującą pojedyncza liczbę naturalną spoza mój automat do lotto. Nie ma logicznego powodu, żeby przy każdym automacie losującym k liczb z n-elementowego zbioru pisać funkcję randomizera od początku. Bo on nie powinien mieć żadnego wpływy na algorytm budujący naszą kolekcję.

Co do kodu, to pozwolisz, że c++ bo mi wygodniej, a i tak może być niezbyt estetycznie bo jednak 5 letnia przerwa źle działa.

Zakładając że mamy podaną funkcję
int losujN (int n);
która zwraca dowolną liczbę naturalną z zakresu 1-49, jak sobie ją wylicza nie jest ważne bo nie tutaj nasze opinie się rozbiegają.

void lotto()
{
_int wynik[6]; // (1)
_int koszyk[49]; // (2)
_for (int n=0; n<49; i++) koszyk[n] = 1; // (3)
_int max = 49; // (4)

_for (int n=0; n < 6; n++) //(5)
_{
___int zakres = max-n; // (6)
___int los = losujN(zakres)-1; /(7)
___wynik[n] = koszyk[los]+los; //(8)
___for (int m=los; m<zakres; m++) koszyk[m]++; //(9)
_}
}

(1) tablica wyników, może być śmietnik, w końcu dopiero wstawimy tam nasze 6 losów
(2) nasz koszyk 49 kulek
(3) wypełniamy tablicę jedynkami, bo wartość losu i określa wyrażenie koszyk[n]+n; stąd też pierwszy element to 1 bo koszyk[0]=0+1, 27 to koszyk[26]=26+1 itd.
(4) stała, dla czytelności kodu dostaje nazwę, bo nie chcąc pakować się w tablice zmiennej wielkości i wskaźniki nie przekazuję tej stałej jako parametr funkcji
(5) robimy przejście po naszej tabeli z wynikami
(6) ustalamy zakres losowania, na różnicę pierwotnej wielkości koszyka i numeru losowanej kuleczki, bo za kolejnym razem mamy jedną kulkę mniej i tak pierwszy zakres to 49-0=49, a ostatni 49-5=44, czyi wszystko się zgadza bo tyle kulek zostało
(7) zapisujemy sobie wartość losu, ponieważ maszyna losująca podaje nam liczby z przedziału 1-n, a indeksowanie tablicy zaczyna się od 0, stąd -1
(8) zapisujemy wynik losu, czyli jak mówi definicja mojej tablicy indeks + wartość jego komórki
(9) clue algorytmu, "wyrzucanie" zbędnej kulki i "przesuwanie" innych w lewo - robimy to właśnie przez zwiększanie wartości komórki

Udostępnij ten post


Link to postu
Udostępnij na innych stronach
Dnia 17.10.2014 o 17:05, ziptofaf napisał:

W sumie to może. Ale tylko jeśli mamy błędne założenia takie jak: /.../


Ale w tym konkretnym przypadku mamy inne założenia, przy których program nie ma prawa się zawiesić w nieskończonej pętli.

Generalnie dobór algorytmu zawsze zależy od okoliczności i konkretnych wymagań. Jeden algorytm lepiej sprawdza się przy dużym zakresie a małej liczbie liczb do wylosowania, inny odwrotnie. Zarzucanie że jakiś algorytm jest zły, bo nie sprawdza się kompletnie odmiennym środowisku jest trochę absurdalne.

Udostępnij ten post


Link to postu
Udostępnij na innych stronach
Dnia 17.10.2014 o 17:39, bVarador napisał:

Ale mogę, sam wskazywałeś na KISS. Więc teraz pozwól mi wyodrębnić funkcję losującą pojedyncza
liczbę naturalną spoza mój automat do lotto. Nie ma logicznego powodu, żeby przy każdym
automacie losującym k liczb z n-elementowego zbioru pisać funkcję randomizera od początku.


Przecież rand() to już jest praktycznie gotowa funkcja randomizera, co ty chcesz jeszcze tutaj pisać?

Anyway, przecież twój kod już na pierwszy rzut oka ma większą średnią złożoność obliczeniową, o większym zużyciu pamięci nie wspominając...

Udostępnij ten post


Link to postu
Udostępnij na innych stronach

A to przecież powiedziałem, że średnio mowa o +-140 przejściach przez tablicę i tyle samo sum. Nigdy też nie twierdziłem, że ten algorytm jest optymalny, bo pewnie mu daleko. Ot pierwszy pomysł na implementację czegoś co jest bezpieczne w dowolnym przypadku.

A co do zużycia pamięci? To na serio? Zaproponuj mi zapamiętujący algorytm bez użycia pamięci, jestem bardzo zaintrygowany. Tylko bez ukrywania stosu pod rekurencją jeśli można, bo to nie przejdzie.

Cały czas tylko twierdzę, że w przeciwieństwie do "porównuj aż będziesz miał co chcesz" jest:
1. przewidywalny
2. nie wysypie się, nigdy

Co do funkcji randomizera to nie chcę nic pisać, chcę odrobinę czytelniejszy kod. Nie widzę powodu, dla którego ktoś czytający implementację mojej funkcji budującej kolekcje, musi zmierzyć się z tego skąd i jakie dane dostaję na źródło. A może nie chcę korzystać z rand() a te losowe cyfry chcę zbudować w oparciu o modulo ilorazu kursu złotego względem 25 walut, średniej wilgotności powietrza w Seulu, liczbą sekund od uderzenia pierwszego samolotu o wierze WTC i kilku innym bezsensownym pomysłom? Co mój automat do lotto ma do tego skąd cyfry z przedziału 1-49 dostanie.

Udostępnij ten post


Link to postu
Udostępnij na innych stronach
Dnia 17.10.2014 o 18:54, bVarador napisał:

A co do zużycia pamięci? To na serio? Zaproponuj mi zapamiętujący algorytm bez użycia
pamięci, jestem bardzo zaintrygowany. Tylko bez ukrywania stosu pod rekurencją jeśli
można, bo to nie przejdzie.

Twój algorytm rezerwuje pamięć proporcjonalnie do zakresu. Nie ma potrzeby pamiętania całego koszyka, aby móc zapamiętać co wylosowałeś. Wyobraź sobie że losujesz 2 liczby z zakresu 0 - 2^32. Nie tylko rośnie ci złożoność obliczeniowa, ale też zużycie pamięci. A przy odpowiednio dużym zakresie program wysypie się przez brak pamięci, więc to rozwiązanie nie jest wcale bezpieczne w każdym przypadku. Ma tylko (przy założonym wcześniej scenariuszu) niższą pesymistyczną złożoność obliczeniową (którą przy algorytmach probabilistycznych i tak się pomija) kosztem ogólnej wydajności.

Udostępnij ten post


Link to postu
Udostępnij na innych stronach

Jeśli chodzi o Gry losowe jak Lotto, Poker to nikt nie używa funkcji Rand bo jest za słaba. Gdyby autor chciał napisać funkcję, która dostanie Certyfikat, że jest losowa to jest to defacto Projekt truniejszy od Lotto xD.

Co do powtórzeń.:
while( Istnieje[x = losuj()]){}// Istnieje może być Słownikiem.
(Ta metoda jest super jak N jest istotnie większe od liczby elementów. Wylosuj unikalnych 45 elementów z 46 troche)

Ewentualnie Losujesz Permutacje 46 liczb i bierzesz 6 pierwszych. Zero sprawdzania.

PS. Jak chesz taką trochę bardziej trickową metodę z pamięcią.(Pamięć M log M gdzie M, to liczba szukanych elementów)
Algorytm:
1. Utwórz pusty set.( Set usuwa duplikaty i wstawia elementy w czasie log M)
2. Dopóki rozmiar Seta < 6
3. Wylosuj i dodaj liczbę ( z indexem = Set.Rozmiar() )do Seta.
4. Wypisz liczby ze set. (Jak sam zaimplementujesz funkcję Compare to za darmo masz wszystko)

PS. @bVarador Twoja Funkcja Lotto to masakra.

Udostępnij ten post


Link to postu
Udostępnij na innych stronach
Dnia 17.10.2014 o 21:49, secretkeeper napisał:

Jeśli chodzi o Gry losowe jak Lotto, Poker to nikt nie używa funkcji Rand bo jest za
słaba. Gdyby autor chciał napisać funkcję, która dostanie Certyfikat, że jest losowa
to jest to defacto Projekt truniejszy od Lotto xD.


Też nie przesadzajmy.
http://www.random.org/clients/
Skorzystać z gotowego API to nie aż taka filozofia ;P

A jakby pisać to samemu to też w sumie nic aż tak trudnego. W kompie czujników temperatury, zegara czy wentylatorów nie brakuje. Więc jeśli wykorzystywać ich kombinację jako ziarno to zapewniamy sobie losowość. Byleby raz na jakiś czas nasz seed odświeżać (choć prawdę mówiąc to sam okres w co lepszych algorytmach i tak wynosi pewnie z 2^9999 więc zanim zaczną nam się powtarzać wyniki to skończy się wszechświat).

Inna sprawa że coś co zapewnia nam poprawny rozkład statystyczny i dobrze tasuje nam karty/lotto/cokolwiek zajmuje... 3 linie kodu.

http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle

Którego implementacja w C# może wyglądać tak:

for (int j = cards.Length - 1; j > 0; j--)
{
int n = rand.Next(j + 1);
Swap(ref cards[j], ref cards[n]);
}

Proste, logiczne i co najważniejsze - działa. Zapewniając że jeśli chcesz potasować np. 6 kart to wszystkie 720 kombinacji pojawi się w zasadzie po równo przy odpowiednio dużej bazie wyników.

Oczywiście, o błąd tutaj nietrudno, zmień z 2 linijki kodu na coś takiego:

for (int j = 0; j < cards.Length; j++)
{
int n = rand.Next(cards.Length);
Swap(ref cards[j], ref cards[n]);
}
I zamiast dla 6 kart gdzie ilość kombinacji to 6!=720 dostajemy 46,656 operacji a niektóre z wyników pojawiają się 1.2-5x częściej niż pozostałe.

Tym niemniej jakkolwiek więc problem losowości w liczbach istnieje to jest on rozwiązywalny nie wymagając przy tym czegoś aż TAK absurdalnie skomplikowanego, wymaga tylko obserwacji jakiegoś zjawiska zewnętrznego. Zaś jeśli chodzi tylko o przetasowanie tablicy to całość jest prosta i daje przewidywalne równomiernie rozłożone wyniki. Byleby wykorzystać już istniejący algorytm a nie pisać własne często bardzo naiwne implementacje. Bo zrobienie tego dobrze wymaga jednak sporej wiedzy z matmy.

Udostępnij ten post


Link to postu
Udostępnij na innych stronach

Dnia 18.10.2014 o 02:48, ziptofaf napisał:

Też nie przesadzajmy.
http://www.random.org/clients/
Skorzystać z gotowego API to nie aż taka filozofia ;P

To był Sarkazm do autora programu. Wnioskując faktu, że pisze lotto i z jakości jego kodu wynika, że dopiero uczy się programować.

Dnia 18.10.2014 o 02:48, ziptofaf napisał:


A jakby pisać to samemu to też w sumie nic aż tak trudnego. W kompie czujników temperatury,
zegara czy wentylatorów nie brakuje. Więc jeśli wykorzystywać ich kombinację jako ziarno

Nie jest trudne. Tylko zauważ, że to początkująca osoba.
Do Pokera się używa Lasera, żeby mieć lepszą losowość w połączeniu z tym co wspomniałeś.

Dnia 18.10.2014 o 02:48, ziptofaf napisał:

Inna sprawa że coś co zapewnia nam poprawny rozkład statystyczny i dobrze tasuje nam
karty/lotto/cokolwiek zajmuje... 3 linie kodu.

Napisałem przecież o permutacjach. Fisher–Yates shuffle, to algorytm do permutacji losowych.
Gotowców początkującym osobą lepiej niedawać. Wystarczyłoby, żeby wpisał w Google permutacje.

>Byleby wykorzystać już

Dnia 18.10.2014 o 02:48, ziptofaf napisał:

istniejący algorytm a nie pisać własne często bardzo naiwne implementacje. Bo zrobienie
tego dobrze wymaga jednak sporej wiedzy z matmy.

Dokładnie. Dodam tylko, że początkujące osoby powinny pisać dużo rzeczy od zera i mniej korzystać z API.

Udostępnij ten post


Link to postu
Udostępnij na innych stronach

Pisze aplikację w ASP.NET i mam problem. W skrócie - mam siatkę 3x3, w której wyświetlają się obrazki. Jeden z nich jest aktywny. Po najechaniu myszką na ten aktywny ma zostać wybrany inny aktywny element. Mam już funkcję losującą ten nowy obrazek (działa), jednak nie wiem jak obsłużyć ''wejście'' kursora w obszar obrazka. Te mam pododawane jako Image z bibliotek .NET. Gdybym zmienił je na ImageMap albo ImageButton mógłbym obsłużyć kliknięcie, ale nie o to chodzi, więc chyba to też mnie nie urządza. Chyba nie pomoże też Hover w CSS, bo nie mam zmieniać obrazka na który najadę, a po prostu wylosować wtedy nowy.

Jako że mam już metodę odpowiedzialną za losowanie, chcę tylko dorobić coś takiego:
1. Kursor najechał na obrazek.
2. Czy obrazek jest aktywny? Jesli tak, losuj nowy obrazek (może być ten sam).

Jakieś pomysły?

PS: Aha, obrazek staje się aktywny po prostu poprzez jego podmienienie na inny.

Udostępnij ten post


Link to postu
Udostępnij na innych stronach

/.../

Nie używam ASP.NET ale... nie możesz niejako nad nieklikalnym obrazkiem dodać klikalne i niewidzialne pole o tym samym rozmiarze czy coś podobnego? W ten sposób kliknięcie na takie pole uruchomi twoją funkcję losującą i efektywnie da to samo co chcesz.

Udostępnij ten post


Link to postu
Udostępnij na innych stronach

Mam taki mały problem. Piszę sobie aplikację w C# i w tej apce mam taką metodę. Ta metoda przyjmuje jako parametr między innymi tablicę obiektów i tablicę bool. Ta metoda wywołuje inną metodę która przyjmuje takie same parametry. Problem polega na tym, że dostaję błędy mówiące mi że nie można przekonwertować typu bool na bool[] i typu NazwaProgramu.nazwaKlasy na NazwaProgramu.nazwaKlasy[]. Z czego może wynikać błąd?

Podaję kod obu metod:

public double Mnozenie(Pizza[] pizza, bool[] ksztalt, int j)
{
double wynik = 0;

if (ksztalt[j])
wynik = (((dlugosc / 2) * (dlugosc / 2) * 3.14) * ilosc) / cena;
else
wynik = (((dlugosc * dlugosc) / 2) * ilosc) / cena;

return wynik;
}

public double Najwieksza(Pizza[] pizza, bool[] ksztalt_pizzy, int d)
{
double suma = 0, temp = 0, ktory = 0;
for (int j = 0; j < d; j++)
{
temp = Mnozenie(pizza[j], ksztalt_pizzy[j], j);
if (suma < temp)
{
suma = temp;
ktory = j+1;
}
}
return suma;
}

Udostępnij ten post


Link to postu
Udostępnij na innych stronach

temp = Mnozenie(pizza[j], ksztalt_pizzy[j], j);

W tym momencie do metody nie przekazujesz całej tablicy, tylko konkretny jej element. Musisz albo przekazać całą tablice, albo w metodzie Mnozenie operować na konkretnych elementach (co ma chyba więcej sensu), a nie na tablicach.

Udostępnij ten post


Link to postu
Udostępnij na innych stronach
Dnia 21.11.2014 o 16:32, Thalanthas napisał:

Tylko jak zrobić by jako parametr był brany tylko konkretny element?


Ale już to robisz - teraz przekazujesz konkretny element do Mnozenie. Problem tkwi w samej metodzie, bo zadeklarowałeś że jako parametry przyjmuje tablice, a powinno być coś w stylu Mnozenie(Pizza pizza, bool ksztalt, int j). Zresztą ja ogólnie nie ogarniam tej metody, nie wiem po co są niektóre parametry i skąd się biorą niektóre wartości - tam jest na pewno dużo do poprawy.

Udostępnij ten post


Link to postu
Udostępnij na innych stronach

Mam problemik z kodem C++

Muszę sprawić, żeby elementy mojej tablicy wyświetlały się w następującym formacie:
44.2
1.1
0.5
(czytaj, z dokładnością do jednego miejsca po przecinku).

Jak to zrobić? Próbowałem z niektórymi manipulatorami, ale one nie pozwalały mi ustawić miejsce po przecinku, tylko ile wyświetli się liczb (setprecision, fixed, etc).

Jesteście w stanie pomóc?

Udostępnij ten post


Link to postu
Udostępnij na innych stronach

Utwórz konto lub zaloguj się, aby skomentować

Musisz być użytkownikiem, aby dodać komentarz

Utwórz konto

Zarejestruj nowe konto na forum. To jest łatwe!


Zarejestruj nowe konto

Zaloguj się

Masz już konto? Zaloguj się.


Zaloguj się
Zaloguj się, aby obserwować