Tässä teille koodaajat kunnon pähkinä purtavaksi. Eli tarkoitus olisi tehdä ohjelma joka täyttää taulukon [100] satunnaisluvuilla väliltä [-25, 25], ja tulostaa taulukon sekä kertymäsumman. Eli pähkinä sinäänsä koska opettajakaan ei tätä osannut.:)
Jos opettaja ei tätä osaa, niin opettaja on *piip*...
Luku välillä [-25,25] saadaan vaikka funktiolla rand() (palauttaa satunnaisen positiivisen kokonaisluvun). Taulukko on sitten toki vaikka signed int (signed char:kin kävisi, jos kaikki luvut ovat välillä [-25,25]). Kun katsot hieman tarkemmin, tuo alue onkin satunnainen luku välillä [0,50] ja josta on vähennetty 25... Eli täyttö menee mekanismilla random(50) - 25. rand()-funktiota käytettäessä katsotaan jakojäännös 51:n kanssa (0...50).
Seuraavassa on taulukon luonti ja täyttö, keksi kertymäsumman lasku itse :)
//inuta header, jossa rand() esitellään. Ja time.h jos //halutaan enemmän satunnaisuutta. signed int taulu[100]; //tai signed char. Alustamisella ei //ole väliä, koska se täytetään kuitenkin. signed int a, luku; //alustetaan rand(), korvaa 12000 time()-funktiolla jos //sisällytit time.h:n (enemmän satunnaisuutta) srand(12000); for(a = 0; a < 100; a++){ taulu[a] = rand()%51 - 25; }
No huhhuh! Opettajalla ei sitten selvästikään ole pätevyyttä :)
Jos ope on nipo, se voi valittaa siitä, että koska RAND_MAX % 51 on noin 26, rand() % 51 tuottaa hieman harvemmin luvut 27:stä ylöspäin. Suhteessa 642/643 suunnilleen. Mitä suurempi jakaja, sitä merkittävämmäksi ongelma kasvaa. Esim. rand() % 22000 tuottaa tuplasti enemmän lukuja 0 - 11000 kuin lukuja 11000 - 22000.
Ja jotta kukaan ei nipota siitä, muutetaan koodi tällaiseksi:
#include <stdlib.h> #include <time.h> int main() { signed int Taulu[100]; srand((unsigned int)time(0)); rand(); // Yksi tyhjä rand(), koska syystä tai toisesta ensimmäinen arvottu luku on aina 0 for (int A = 0; A < 100; A++) Taulu[A] = 51 * rand() / RAND_MAX - 25; // Ja tänne nuo kertymäsummat ja muut return 0; }
Integer jakolaskujen kanssa kannattaa sitten olla tarkkana :)
Metabolixinkin kannattaa tarkistaa tuleeko
51 * rand() / RAND_MAX
lausekkeesta joskus jotain muuta kuin nollaa. Mun mielestä se kaipaa double-castia johonkin väliin.
FooBat kirjoitti:
Metabolixinkin kannattaa tarkistaa - -
Eipä tarvitsekaan :)
51 * rand() = [0 .. 51 * RAND_MAX] = [0 .. 1671117] (VC++:lla ainakin)
[0 * RAND_MAX .. 1 * RAND_MAX - 1] / RAND_MAX = 0
[1 * RAND_MAX .. 2 * RAND_MAX - 1] / RAND_MAX = 1
[2 * RAND_MAX .. 3 * RAND_MAX - 1] / RAND_MAX = 2
jne.
Eli kyllä tämä toimii, kun vain on laskujärjestys oikein. Sen sijaan rand() / RAND_MAX * 51
vaatii double-/float-castin jakolaskuun, koska jakolasku tehdään esnin, ja kokonaisluvuilla siitä on 1/RAND_MAX mahdollisuus saada 1, muuten tulee 0.
Käsittääkseni joissakin ympäristöissä tuo RAND_MAX on hyvin lähellä MAX_INT:iä (vai mikä se nyt onkaan), jolloin käytännössä
51*MAX_INT <= MAX_INT ja
x / MAX_INT = 0, kun x < MAX_INT
En myöskään muista oliko C:ssä laskujärjestys tarkoin määrätty tapahtuvaksi vasemmalta oikealle. Periaatteessahan kerto ja jakolasku ovat saman arvoisia ja jotkut hassut kääntäjät voivat tehdä asian vähän eri lailla.
Näköjään tosiaan ainakin joissakin UNIX-pohjaisissa. Siinä näyttäisi kattona olevan 0x7fffffff eli 2 ^31 - 1. No, kai sitä pitää vaivautua tekemään porttautuvaa koodia. Tuosta laskujärjestyksestä veikkaisin, että ainakin sulkujen lisääminen varmaankin varmistaa oikean järjestyksen.
Edit: Tässä uusi hieno Random-funktioni (ehdot toimivat ainakin VC++:lla):
int Random(int Range) { #if (RAND_MAX == 0x7fff) // RAND_MAX = 2^15, joten kahdella saadaan luvut 0 - 2^30. // Yhtä käyttäen isoissa arvonnoissa joitakin lukuja ei tulisi koskaan- return (int)(Range * ((rand() | (rand() << 15)) / 1073741824.0)); #else #if (RAND_MAX == 0x7fffffff) // jos RAND_MAX on 2^31, se riittää sellaisenaan return (int)(Range * (rand() / 2147483648.0)); #else // jos RAND_MAX on jotakin muuta, arvotaan suoraan return (int)(Range * (rand() / (double)RAND_MAX)); #endif #endif } // Random(10) palauttaa siis luvun 0 - 9
Aihe on jo aika vanha, joten et voi enää vastata siihen.