Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: C++: Suurien lukujen laskeminen

Sivun loppuun

bug85 [23.03.2007 23:17:03]

#

Terve!

olisi sellainen ongelma c-kielellä ohjelmoitaessa,että pitäisi saada ohjelma laskemaan lukuja joiden pituus on jopa 1000 numeroa(luvut luetaan tiedostosta),eli kysymyksessä on todella suuret luvut.
miten tämä onnistuisi? - kokeilin itse jo laskea long doublen avulla mutta en saanu sitä toimimaan,vaikka long doublenhan vissiin pitäisi näyttää lukuja suurinpiirtein 10^4000 asti.jos se onnistuu long doublen avulla niin miten luku ensin määritellään ja miten se tulostetaan?kokeilen jo tälleen menestymättä:

long double arvo1=0.0;
long double arvo2=0.0;
long double tulos =0.0;
scanf("%Lf", &arvo1)
scanf("%Lf", &arvo2);
tulos = arvo1+arvo2;
printf("%Lf", tulos);

tämä tulostaa tulokseksi nolla, ja scanf lukee lukuja vain ensimmäiset 17 kunnolla,loput on nollia(tai 17 numeron jälkeen luvut 0 tai vääriä).
eli kysymys kuuluu, että onko minulla jotain määrittelyissä vikana,vai onko sitten jokin muu keino laskea tiedossa olevia lukuja(yhteen,jakaa,kertoa tai vähentää).
(ohjelmana on dev-c++ ja codeBlocks uusimmat versiot- molemmilla sama ongelma)

kiitos kaikille,jotka jaksoitte lukea romaanini! :)

(Mod. Edit. Ja kiitos kaikille, jotka käytätte jatkossa kooditageja. ;))

sqwiik [23.03.2007 23:36:45]

#

Haulla löytyi seuraavat keskustelut, joista luulisi olevan hyötyä:
Eka ja toka.

Molemmissa päädyttiin apukirjastojen käyttöön, että siitä.

tn [24.03.2007 00:11:56]

#

Voithan myös lukea luvun manuaalisesti. Tässä tapauksessa täytyy tietenkin ottaa huomioon, että vaikka long double ehkä pystyykin käsittelemään noin isoja lukuja, ei sen tarkkuus ole kuin muutama kymmenen desimaalia (merkitsevää numeroa).

sqwiik [24.03.2007 14:12:38]

#

Jos laskettavat luvut ovat kokonaislukuja (eivätkä siis liukulukuja kuten viimeksi oletin), laskut voi suorittaa merkkijonojen avulla.

FooBat [24.03.2007 15:02:08]

#

Tuo tulostuksen virheellisyys taitaa johtua tuosta printf-funktiosta eikä niinkään luvuista. Itselläniki on ollut ongelmia tulostaa long double tyyppisiä muuttujia. Tuo "%Lf" ei vaan tunnu toimivan oikein, vaan se näyttää käyttävän muuttujaan siten kuin siinä olisi vähemmän bittejä kuin siinä oikeasti on. Ehkä ongelma johtuu käännösympäritöstäni (Window XP, vanha gcc), ehkä jostain muusta. Jos long double tyyppiä tarvitaan vain laskennan tarkkuuden takia eikä niinkään arvoalueen, voidaan se castata double tyyppiseksi ennen tulostusta ja tulostaa "%lf"-määreellä, joka tuntuisi toimivan.

bug85 [25.03.2007 20:01:00]

#

sqwiik kirjoitti:

Jos laskettavat luvut ovat kokonaislukuja (eivätkä siis liukulukuja kuten viimeksi oletin), laskut voi suorittaa merkkijonojen avulla.

mitenhän tuo merkkijonojen avulla laskeminen oikein tapahtuu..?kehtaisko joku hieman jeesata.eli kysymys on kokonaisluvuista. :)

TsaTsaTsaa [25.03.2007 20:27:02]

#

EDIT: Taisikin olla c eikä c++. No eiköhän tätä voi soveltaa jotenkin.

Heitelläänpä hatusta jonkin näköistä purkkaa. En tiedä toimivuudesta ja tehokkuudesta.

Niin ja tarvitset funktiot int2char() ja char2int().

string luku1 = "98934859835478237894792837489789675498";
string luku2 = "12738476854900000000000000000000435665";
string tulos;

// Pari apuria
int iapu = 0;
int iapu2 = 0;
char capu = 'c';

// Selvitetään, kumpi luvuista lyhyempi
for (int i = 0 ; i < lyhyempi.length() ; ++i)
{
   // Lasketaan lukujen vastinnumerot yhteen
   iapu = iapu2 + char2int( lyhyempi.at( lyhyempi.length()-i ) )
          + char2int( pitempi.at( pitempi.length()-i ) );
   iapu2 = 0;
   // Menikö yli kympin?
   if ( iapu >= 10 )
   {
      // Ykköset kirjaimiksi
      capu = int2char( iapu % 10 );
      // Ylimääräinen kymppi talteen seuraavaa kierrosta varten
      iapu2 = 1;
   }
   else
   {
      capu = int2char( iapu );
   }
   // Lisätään saatu numero tulokseen
   tulos = capu + tulos;
}

// Jos iapu2:een jäi ykkönen, tässä vaiheessa pitää se lisätä tulokseen
// oikean merkin kohdalle mutta en jaksa.

// Muuten laitetaan tuloksen alkuun pidemmästä luvusta alkupätkä
tulos = pidempi.substr(0, pidempi.length()-lyhyempi.length() ) + tulos;

bug85 [25.03.2007 21:01:52]

#

olen vielä tosi noviisi ohjelmoinnissa niin eipä hirveästi aukene toi TsaTsaTsaan esimerkki,koska se on C++:lla..mutta jos joku kehtais tehdä tuon funktion tai ohjelman pätkän c:llä niin olisin todella kiitollinen, ja oppisi itsekkin vähän lisää noista silmukoista ja merkkijonoista.
kiitos jo etukäteen..!

bug85 [25.03.2007 22:04:51]

#

Ja mitähän toi manuaalinen luvun lukeminen tarkoittaa?

tn [25.03.2007 22:22:45]

#

bug85 kirjoitti:

Ja mitähän toi manuaalinen luvun lukeminen tarkoittaa?

Jos kysymys koski minun viestiäni, niin siinä tapauksessa se tarkoittaa sitä, että et lue lukuja suoraan scanf-funktiolla, jos se ei kerran siihen pysty. Sen sijaan luet luvut sisään merkkijonoina ja muunnat itse ne merkkojonoista luvuiksi. Tulostamisen kanssa lienee sama homma.

Mutta kuten sanoin, sisäänrakennetut liukulukutyypit pystyvät varastoimaan luvun vain muutaman kymmenen merkitsevimmän numeron tarkkuudella, ei tässä välttämättä ole paljoakaan järkeä. Siispä vaikkapa luvut:
100000000000000000000000000000000000000000000000000000000000000000000000000000 ja
100000000000000000000000000000000000000000000000000000000000000000000000000001
(luultavasti myös vaikkapa 100000000000000000000000000000000000000000000000000000000000000007644916352311)
muuttuvat mitä suurimmalla todennäköisyydellä samaksi luvuksi long doubleksi muutettaessa. Näin ollen kummatkin (ne kaikki) voisi syöttää sisään parinkymmenen desimaalin tarkkuudella (tässä tapauksessa riittää yksi desimaali) kymmenpotenssimuodossa: 1E77.

FooBat [25.03.2007 22:23:27]

#

bug85 kirjoitti:

Ja mitähän toi manuaalinen luvun lukeminen tarkoittaa?

Vaikka sitä, että sinulla on taulukoissa/merkkijonoissa laskettavien lukujen numerot/merkit ja sitten toteutat yhteenlaskut tai kertoalgoritmin itse käyttäen näitä numeroita/merkkejä. Esim. yhteenlaskussa lasket lukujen numerot erikseen yhteen ja jos arvo on yli kymmenen, lisäät seuraavaan numeroon yhden ja vähennät nykyisestä 10. Tästä ideasta voi tietenkin kehittää hiukan tehokkaampia ratkaisuja, jotka laskevat useampia numeroita kerralla.

sqwiik [25.03.2007 22:27:11]

#

C:llä kirjoitettuna käytetään sitten char-taulukkoa. Sitten vain ynnäämään lukuja ala-asteella opitulla tavalla ^_-.

Tässä koodia, mutta funktiomuotoa en jaksa vääntää, ja tässä lasketaan vain luvut yhteen. Kerto- ja jakolaskutkin voi rakentaa samalla periaatteella (kerto- ja jakopuu).

/* Nämä on sitten varattu jollain tavalla & luettu tiedostosta */
char * luku1 = "192136412821374192342374235763240957236523";
char * luku2 = "673529478374963274628356834857324562389";
char tulos[1000];

int len1 = strlen(luku1), len2 = strlen(luku2), pituus, lyhyt, muisti = 0, a, vali;
/* Pidempi & lyhyempi */
pituus = (len2>len1)?len2:len1;
lyhyt = (len2<len1)?len2:len1;

tulos[pituus] = '\0'; /* Lopetusmerkki valmiiksi */

for(a = 0; a < pituus; a++){
  /* Numeroita otetaan lyhyemmästä luvusta */
  if(a < lyhyt){
    /* - '0' jotta saadaan oikea numeerinen arvo + <muisti> */
    vali = (luku1[len1 - a] - '0') + (luku2[len2 - a] - '0') + muisti;
  /* Lyhyempi luku loppunut; nyt lisätään enää 0:ia lyhyemmän numeroiden sijasta. */
  }else{
    /* luku1 on pitempi */
    if(len1 == pituus)vali = (luku1[len1 - a] - '0') + muisti;
    else vali = (luku2[len2 - a] - '0') + muisti;
  }
  /* Jos menee yli 10, niin siirto muistiin seuraavaa kierrosta varten; muuten 0. */
  if(vali > 10){
    vali -= 10;
    muisti = 1;
  }else muisti = 0;

  tulos[pituus - a] = vali + '0';
}

Koodin toimivuudesta ei takeita.

tn [25.03.2007 22:31:08]

#

Kertoisitko muuten, onko kyse kokonaisluvuista vai reaaliluvuista(liukuluvuista)? Entä täytyykö lukujen säilyä täysin tarkkoina, ja jos ei, niin kuinka tarkkoina? Tässä ketjussa asiaa on käsitelty vähän siltä sun tältä kantilta.

sqwiik [25.03.2007 22:42:09]

#

bug85 kirjoitti:

...Eli kysymys on kokonaisluvuista. :)

.

Ja mielestäni tuloksen täytyy säilyä tarkkana.

tn [26.03.2007 00:52:13]

#

sqwiik kirjoitti:

bug85 kirjoitti:

...Eli kysymys on kokonaisluvuista. :)

.

Ja mielestäni tuloksen täytyy säilyä tarkkana.

No hups, pitäisi lukea tarkemmin näitä ketjuja. Siinä tapauksessa minun hölinäni noista liukuluvuista saa unohtaa (lukuunottamatta sitä kuuluisaa manuaalista lukemista).

Jos tuloksen tosiaan täyttyy olla "tarkka" (kuten yleensä kokonaislukujen ollessa kyseessä on tapana), lienevät ainoat mahdollisuudet tosiaan käyttää valmiita kirjastoja tai koodata itse omat laskutoimitus- ja io-rutiinit.

Metabolix [26.03.2007 01:07:36]

#

GMP on ihan käyttökelpoinen kirjasto varmasti C:llekin, vaikka en olekaan itse siitä käyttänyt kuin C++-wrappereita. Jos ei ole tarkoitus keksiä pyörää uudelleen, ei turhaan kannata. (Tietenkin toisinaan on mukava keksiä pyöriä sun muita uudestaan.) Dev-C++:n kanssa kannattaa tietenkin vilkaista, josko tuolle löytyisi Dev-Pak, niin onnistuisi asentaminen vaivatta.

bug85 [26.03.2007 07:36:29]

#

Kiitoksia oikein paljon kaikille.Tosi hienoa,että toisia autetaan ku ollaan hädässä.. :)
EDIT:En ite ainakaan saa toimimaan tuota sqwiikin ohjelmaa kun se tulostaa tulokseksi mitä sattuun.mutta pitänee vielä yrittää.

bug85 [26.03.2007 15:23:25]

#

eli nyt sain toimimaan tuo sqwiikin esimerkin.nyt pitäis vielä värkätä samanlaiset mutta vähennys-,jako- ja kertolaskuja laskevat.


Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta