Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: C++: Alkulukuetsijä

Baglair [30.11.2005 18:07:54]

#

Sain valmiiksi tekemäni alkulukuetsijän C:llä. Bugeja sun muuta siitä varmasti löytyy vaikka millä mitalla. Olen melko varma että on olemassa paljon helpompia reittejä toteuttaa tuo. Onko siis mitään parannusehdotuksia. En ole kovin hyvä ohjelmoija vielä, joten jos koodissa on joitakin virheitä se johtuu siitä.

//Alkulukujen etsijä
//Tämän ohjelman tarkoitus on etsiä alkuluvut, jotka löytyvät halutulta väliltä.
//Toivoisin kuitenkin, että et sijoita lukujen kohdille liian isoa numeroa, silloin toimivuutta ei ole taattu.
//Alkuluvuksi luokitellan luku, joka on jaollinen vain itsellään ja yhdellä.
#include <stdio.h>

//Määritykset:
#define	LUKU_MIN	0	//Minimi luku miltä väliltä alkulukuja etsitään.
#define	LUKU_MAX	100	//Maksimi luku miltä väliltä alkulukuja etsitään.
#define	RIVI_MAX	5	//Montako lukua per rivi.
#define	ALKUVUT_MAX	200	//Mahdollistaa 201 alkuluvun tallentamisen.

main()
{
//JUMP_MODE:a tarvitaan jos LUKU_MIN > 0, jotta ohjelma ei tulostaisi ei haluttuja alkulukuja.
	char JUMP_MODE = 0, ALKULUKU = 1;

//alkuluvut[200] mahdollistaa vain 201 alkuluvun tallentamisen.
	int x = 0, y = 0, luku = 2, rivi = 0, alkuluvut[ALKUVUT_MAX];

//Määritetään ensimmäinen alkuluku turhan koodin välttämiseksi ja tehdään se tällä tyylillä selveyden vuoksi.
	alkuluvut[0] = 2;

//Poikkeuksena täytyy printata 2 jotta vältytään ylimääräiseltä koodilta
	if(LUKU_MIN <= 2)	{
		printf("%5d", 2);
		++rivi;
	}

//Etsitään alkulukuja
	for(; luku <= LUKU_MAX; ++luku)	{
//Jos luku on jaettavissa joillakin edellisitä alkuluvuista, luku ei ole alkuluku.
		for(; y >= 0; --y)	{
			if(luku % alkuluvut[y] == 0)	{
				ALKULUKU = 0;
				break;
			}
		}

//Tämän käydessä toteen luku on alkuluku.
			if(ALKULUKU == 1)	{
				++x;
				if(luku < LUKU_MIN)	{
					alkuluvut[x] = luku;
					JUMP_MODE = 1;	//Asetetaan hyppytila tulostuksen ohitusta varten.
				}
				if(JUMP_MODE != 1)	{
					alkuluvut[x] = luku;
					printf("%5d", luku);
					++rivi;

				if(rivi >= RIVI_MAX)	{
					printf("\n");
					rivi = 0;
					}
				}
			}
			y = x;
		if(JUMP_MODE == 1)
			JUMP_MODE = 0;

		if(ALKULUKU == 0)
			ALKULUKU = 1;

		if(y >= ALKUVUT_MAX)	{
			printf("\nNo nyt loppui tallennustila alkuluvuille");
			return 0;
		}
	}
}

Sami [30.11.2005 18:16:43]

#

Ainakin sellainen helppo optimointi olisi mahdollinen, että kokeilet jakaa lukua ainoastaan sen neliöjuurta pienemmillä alkuluvuilla. Jos luku ei ole jaollinen millään näistä luvuista, niin se on alkuluku, muuten ei.

Metabolix [30.11.2005 21:44:09]

#

int Alkuluvut(int Max_Lukuja, int * Lukutaulu, int Max_Luku = 0x7fffffff)
{
    int a, Luku, Lisays, Viimeinen, Lukuja;

    // Max_Lukuja ei voi olla suurempi kuin Max_Luku
    if (Max_Lukuja > Max_Luku)
        Max_Lukuja = Max_Luku;

    // Ettei se ole liian pieni...
    if (Max_Lukuja < 1) return 0;
    Lukutaulu[0] = 2;
    if (Max_Lukuja < 2) return 1;
    Lukutaulu[1] = 3;
    if (Max_Lukuja < 2) return 2;

    // Kaksi lukua valmiina (2 ja 3)
    Lukuja = 2;
    Lisays = 2;
    Viimeinen = 0;
    Luku = 5;

    while (Lukuja < Max_Lukuja && Luku < Max_Luku)
    {
        // Jakotesti vain neliöjuureen asti
        while (Lukutaulu[Viimeinen] * Lukutaulu[Viimeinen] <= Luku)
            Viimeinen++;

        // Jakotestit (kakkosta ei tarvita)
        for (a = 1; a < Viimeinen; a++)
        {
            if (Luku % Lukutaulu[a] == 0)
            {
                a = -1;
                break;
            }
        }

        // a == -1, jos tuli break
        if (a != -1)
        {
            Lukutaulu[Lukuja] = Luku;
            Lukuja++;
        }

        // Lisätään ja muutetaan lisäystä
        Luku += Lisays;
        Lisays ^= (4 | 2);
    }

    return Lukuja;
}

// Esimerkki
int Luvut[100];
int Alkulukuja = Alkuluvut(100, Luvut);

Siinä pieni funktio. Kaikki tavalliset optimoinnit tehty, eli kakkosella jaollisia ei edes kokeilla. Lisäksi vielä joka kolmas jäljelle jäävistä hypätään yli:

        1   2   3   4
5   6   7   8   9   10
11  12  13  14  15  16
17  18  19  20  21  22

Sarakkeet 2, 4 ja 6 ovat 2:lla jaollisia. Sarakkeet 2 ja 5 ovat 3:lla jaollisia. Jäljelle jäävät sarakkeet 1 ja 3. Siitä siis tuo Lisays-muuttujan käyttö.

Heikki [30.11.2005 21:57:58]

#

Itse olen tehny tällaisen, pikatesteillä selvästi nopeampi kuin Metabolixin (laskee 1 000 000 alkulukua ajassa 0,185s, kun Metabolixin vie 21s). Algoritmi on Erastostheneen seula.

#include <iostream>
using namespace std;

int main(int arg, char *argv[]) {

	long long int lukuja;
	if (arg>1) {
		lukuja=atoi(argv[1]);
	} else {
		cout << "Kuinka pitkälle lasketaan?\n";
		cin >> lukuja;
	}

	bool *luku=new bool[lukuja]; //true = ei ole alkuluku, false=alkuluku
	long long int i;
	cout << "\nMuistia kuluu " << sizeof(luku)*lukuja << " tavua ( " << (sizeof(luku)*lukuja)/1024 << " kt) \n\n";
	//alustetaan
	for (i=0; i<lukuja; i++)
		luku[i]=false;

	for (i=2; i<=lukuja; i++) {
		//onko i jo merkitty ei_alkuluvuksi
		if (luku[i]==false) {
			//lukujonon kaikki i:llä jaolliset luvut paitsi i merkataan
			for (long long int n=2*i; n<lukuja; n+=i)
				luku[n]=true;
		}
	}


	char vastaus;
	if (arg==1) {
		cout << "\nHaluatko nähdä alkuluvut k/e?\n";
		vastaus; cin >> vastaus;
	}
	if (vastaus=='k') {
		cout << "Löydettiin seuraavat alkuluvut: \n";
		for (i=2; i<lukuja; i++) {
			if (luku[i]==false){
				cout << i << "\n";
			}
		}
	}


	delete[] luku;
	luku=0;

	return 0;
}

Metabolix [30.11.2005 22:24:42]

#

Heikki: Varmasti on nopeampi, joo, mutta laitapa se laskemaan alkuluvut neljään miljardiin asti. Siinä missä minun tapani vie muistia vain löytyneiden lukujen verran (siitä voi luonnollisesti tehdä dynaamisen), tuo vie muistia aivan mielettömästi. Minulla ainakin bool vie koko tavun.

Tietenkin paras tapa olisi laskea tuolla jonnekin asti ja tavallisemmalla tavalla vielä eteenpäin. Ja varmasti joku matemaatikko osaisi kertoa vielä joitakin optimointimahdollisuuksia.

Heikki [01.12.2005 07:02:15]

#

Juu tiedän kyllä muistinkulutuksen, piti itse asiassa tuossa viestissäkin jo sanoa että muistin kanssa alkaa olemaan vähän ongelmia isompia määriä laskettaessa. Testasin tuota miljardin laskemista ja seurailin muistinkäyttöä, hyvä että swappi riitti :) Pienillä määrillä (eli miljooniin asti) tuo on kuitenkin ihan toimiva.

Edit. Itse asiassa kun nyt rupesin miettimään niin ei tainnutkaan muisti ja swappi riittää. Sizeof taulukosta sanoi n. 3gt, rammia on 768mt ja swappia giga. En sitten tiedä miten tuo toimi, en jaksanut odottaa lopuun asti.

Vastaus

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

Tietoa sivustosta