Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: C++: Vektorien tarkastelu

Sivun loppuun

cshele [19.02.2021 12:42:33]

#

Olen tässä aloittelevana ohjelmointiharjoittelijana aloittanut taulukoiden käyttöä ohjelmoinnissa. Muuten homma vaikuttaa selvältä, mutta miten saan tarkasteltua kahden eri vektorin sisältämien lukujen yhtäläisyyksiä?

Jos on esimerkiksi nk. lottokone, johon käyttäjä syöttää oman rivinsä, vaikka rivi[7] ja ohjelma sitten arpoo oikeat luvut lottonumero[10], jossa paikat 0-6 on päänumerot ja 7-9 lisänumerot. Arvotuissa luvuissa ei sallita duplikaatteja.

Eli siis ohjelman loppuun haluaisin vielä tulostaa, montako pää- ja lisänumeroa käyttäjä arvasi oikein syöttämässään rivissä. Tällä hetkellä ohjelma on täysin toimiva siihen asti, että käyttäjä on syöttänyt rivinsä ja ohjelma arpoo ensin paikat 0-6 ja sitten 7-9.

Metabolix [19.02.2021 12:50:31]

#

Tarkastuksen voi tehdä kahdella silmukalla: käy läpi käyttäjän antamat luvut, ja joka luvun kohdalla käy läpi kaikki arvotut luvut vertailua varten.

Kun päänumerot ja lisänumerot käsitellään eri tavalla, voi olla järkevää laittaa ne eri taulukoihin, jotta ei tarvitse miettiä oikeita indeksejä. Kahdella erillisellä taulukolla haluamasi tarkastus onnistuisi näin:

for (auto oma: rivi) {
  for (auto oikea: loton_paanumerot) {
    if (oma == oikea) merkkaa_kirjanpitoon_yksi_oikea_numero();
  }
  for (auto oikea: loton_lisanumerot) {
    if (oma == oikea) merkkaa_kirjanpitoon_yksi_oikea_lisanumero();
  }
}

Toisaalta tietysti arvontavaiheessa nämä käsitellään samalla tavalla, joten siinä vaiheessa erillinen taulukko tuottaa lisävaivaa. Molempia tapoja voi käyttää.

cshele [19.02.2021 12:54:20]

#

Kiitos vinkistä, koitan sovittaa tuon koodiini.

Koitin aluksi laittaa pää- ja lisänumerot eri taulukoihin, mutta sen jälkeen en osaa enää eliminoida lisänumeroista duplikaatteja suhteessa päänumeroihin.

Metabolix [19.02.2021 13:00:33]

#

Kun käytät yhtä taulukkoa, joudut tekemään sisemmän for-silmukan perinteisesti indeksillä (i = 0; i < 10; ++i), jotta pystyt sitten i:n perusteella katsomaan, onko kyseessä lisänumero.

cshele kirjoitti:

Koitin aluksi laittaa pää- ja lisänumerot eri taulukoihin, mutta sen jälkeen en osaa enää eliminoida lisänumeroista duplikaatteja suhteessa päänumeroihin.

Ohjelmoinnissa on aina monta mahdollisuutta. Yksi ratkaisu tähän ongelmaan on myös se, että vasta arvonnan jälkeen kopioisit luvut eri taulukoihin. Toinen ratkaisu on arvonnassa

The Alchemist [19.02.2021 13:55:43]

#

Silloin kun opiskellaan aivan alkeita ja vielä hankalalla kielellä kuten C++, niin minusta on aivan puhdasta ajanhukkaa yrittää pähkäillä triviaaleja ongelmia itse. Silloin ei opi yhtään mitään ja pahimmassa tapauksessa ohjelmointi alkaa maistua puulta, kun tulosta ei kerry.

Suosittelen etsimään esimerkkejä netistä ja vaikka koodaamaan ne omin käsin ja miettimään niitä.

https://www.geeksforgeeks.org/find-union-and-intersection-of-two-unsorted-arrays/

cshele [19.02.2021 14:33:35]

#

Metabolix kirjoitti:

Ohjelmoinnissa on aina monta mahdollisuutta. Yksi ratkaisu tähän ongelmaan on myös se, että vasta arvonnan jälkeen kopioisit luvut eri taulukoihin. Toinen ratkaisu on arvonnassa

Miten tämä arvonnan jälkeinen eri taulukoihin jakaminen tapahtuu? Se vaikuttaisi helpoimmin ymmärrettävältä, ja sitten tuo jo näyttämäsi vertailusilmukka. Sain siitä osan toimimaan, mutta vielä juurikin jaottelu puuttuu. Tuleeko se jotenkin tuon duplikaattirajauksen sisälle?

int main()
{
	setlocale(LC_ALL, "fi_FI");
	srand(time(NULL));


	int lottonumero[10] = { 0 }, rivi[7];
	int paa;
	bool duplikaatti;
	int oma, oikein1, oikein2;
	int laskuri = 0;
	int laskuri2 = 0;


	cout << "Anna lottorivisi, seitsemän eri lukua väliltä 1-39!\t";

	for (int i = 0; i < 7; i++)
	{
		cin >> rivi[i];
		cout << "\t";

		int oma = rivi[i];
	}

	cout << endl;

//******************************************************************************
	for(int i=0; i<10; i++)
	{
		do
		{
			paa = 1 + rand() % 39;
			duplikaatti = false;

			for (int j = 0; j < i; j++)
			{
				if (paa == lottonumero[j])
				{
					duplikaatti = true;
					break;
				}
			}
		} while (duplikaatti == true);

		lottonumero[i] = paa;

	}

	cout << "Päänumerot ovat: \t";

	for (int i = 0; i < 7; i++)
	{
		cout << lottonumero[i] << "\t";

	}

//**************************************************************************


	cout << endl << "Lisänumerot ovat: \t";

	for (int k = 7; k < 10; k++)
	{
		cout << lottonumero[k] << "\t";
	}
	cout << endl;

	for (auto oma : rivi)
	{
		for (auto oikein1 : lottonumero)
		{
			if (oma == oikein1) laskuri++;
		}
		for(auto oikein1 : lisanumero)
		{
			if (oma == oikein1) laskuri2++;
		}
	}

	//********************************************************************

	cout << "Päänumeroita oikein:\t" << laskuri <<endl;

	cout << "Lisänumeroita oikein:\t" << laskuri2 << endl;

	system("pause");
	return 0;
}

carabia [19.02.2021 15:02:26]

#

Yksi simppeli tapa ratkaista duplikaattien arvonta on taulun ns. "swap and pop" operaatio, jossa taulusta poistetaan numero siirtämällä taulun viimeinen alkio sen tilalle, jolloin taulun data saadaan pidettyä yhtäjaksoisena. Toki metodi vaatii, että arvottavat numerot sijoitetaan johkin tauluun valmiiksi. Numeroiden arpominen sujuisi näin (esimerkkinä arvotaan kolme päänumeroa, ja kaksi lisänumeroa numeroista 1-10):

// ...

int numerotaulu[10] = {1,2,3,4,5,6,7,8,9,10};
int taulun_koko = 10;

int paanumerot[3];
int lisanumerot[2];

// Päänumerot
for (int i = 0; i < 3; i++) {
    // Arvotaan indeksi numerotaulusta
    int n = rand() % taulun_koko;

    paanumerot[i] = numerotaulu[n];

    // "Swap and pop"
    taulun_koko--;
    numerotaulu[n] = numerotaulu[taulun_koko];
}

// Lisänumerot, sama homma.
for (int i = 0; i < 2; i++) {
    int n = rand() % taulun_koko;
    lisanumerot[i] = numerotaulu[n];
    taulun_koko--;
    numerotaulu[n] = numerotaulu[taulun_koko];
}

EDIT: Taulun "koko" on periaatteessa implisiittinen, mutta käytin siihen muuttujaa esittääkseni idean toivottavasti hieman selvemmin näin.

cshele [19.02.2021 15:31:46]

#

Kokeilin alustaa arvonnan jälkeen uudet taulukot päänumero[7]={{lottonumero[0]},.....{lottonumero­[6]}}; ja lisänumero[3]={{lottonumero[7]}jne.};

silläkin toimi, en tosin tiedä, jos se on kömpelö tapa ohjelmoida taulukon jako. Näin aloittelijalle se tuntui loogiselta ja helposti ymmärrettävältä.

Kiitos vinkeistä, ilman olisi mennyt paljon kauemmin löytää ratkaisu. :)

Grez [19.02.2021 18:36:12]

#

Tuon voisi toteuttaa myös näin :D

int main()
{
	srand(time(NULL));

	int64_t omat = 0, arvotut = 0;

	cout << "Anna lottorivisi, seitsemän eri lukua väliltä 1-39!\t";

	int luku;
	for (int i = 0; i < 7; i++)
	{
		cin >> luku;
		if (luku < 1 || luku > 39) {
			cout << "ei kelpaa";
			i--;
		}
		else {
			omat |= ((int64_t)1 << luku);
		}
		cout << "\t";
	}
	cout << endl;

	int oikein[2] = { 0,0 };

	for (int i = 0; i < 10; i++)
	{
		int arvottu = 1 + rand() % 39;
		int flag = (int64_t)1 << arvottu;
		if (arvotut & flag) {
			i--;
			continue;
		}
		arvotut |= flag;
		if (i == 0) { cout << "Päänumerot ovat: \t"; }
		else if (i == 7) { cout << endl << "Lisänumerot ovat: \t"; }
		cout << arvottu << "\t";

		if (omat & flag) { oikein[i < 7 ? 0 : 1]++; }
	}

	cout << endl << "Päänumeroita oikein:\t" << oikein[0] << endl;
	cout << "Lisänumeroita oikein:\t" << oikein[1] << endl << "Paina enter";
	cin >> luku;
	return 0;
}

Metabolix [19.02.2021 23:02:36]

#

cshele kirjoitti:

Kokeilin alustaa arvonnan jälkeen uudet taulukot

Se on tässä tapauksessa ihan toimiva vaihtoehto. Usein käytettäisiin kuitenkin silmukkaa, eli for-silmukalla kopioitaisiin luvut uuteen taulukkoon.

Grez kirjoitti:

Tuon voisi toteuttaa myös näin :D

Tai näin 8D

#include <iostream>
#include <set>
#include <iterator>
#include <algorithm>
#include <random>

template <typename T> std::insert_iterator<T> end_inserter(T& t) {
	return std::inserter(t, t.end());
}

int main() {
	auto gen = std::mt19937{std::random_device{}()};
	std::set<int> pallot, arvotut, oikeat, lisat, omat;
	std::generate_n(end_inserter(pallot), 39, [&]{ return pallot.size() + 1; });
	std::ranges::sample(pallot, end_inserter(arvotut), 10, gen);
	std::ranges::sample(arvotut, end_inserter(oikeat), 7, gen);
	std::ranges::set_difference(arvotut, oikeat, end_inserter(lisat));

	std::cout << "Anna lottorivisi eli seitsemän lukua väliltä 1–39: ";
	std::copy_n(std::istream_iterator<int>(std::cin), 7, end_inserter(omat));

	int oikeita = 0, lisanumeroita = 0;
	for (auto luku: omat) {
		if (oikeat.count(luku)) {
			oikeita += 1;
		} else if (lisat.count(luku)) {
			lisanumeroita += 1;
		}
	}

	std::cout << "Oikea rivi: ";
	std::ranges::copy(oikeat, std::ostream_iterator<int>(std::cout, ", "));
	std::cout << "lisänumerot ";
	std::ranges::copy(lisat, std::ostream_iterator<int>(std::cout, ", "));
	std::cout << "tosin tämä lotto taitaa olla jo vanhentunut!\n";

	std::cout << "Oma rivisi: ";
	std::ranges::copy(omat, std::ostream_iterator<int>(std::cout, ", "));
	std::cout << "ja tulos on " << oikeita << " + " << lisanumeroita << ".\n";
}

Tai jos syötteen haluaa käsitellä paremmin, siihen voi laittaa näin

// ...
	std::cout << "Anna lottorivisi eli seitsemän lukua väliltä 1–39: ";
	while (omat.size() < 7) {
		int luku;
		if (std::cin >> luku) {
			if (!pallot.count(luku)) {
				std::cout << luku << " ei kelpaa.\n";
			} else if (omat.count(luku)) {
				std::cout << luku << " on jo valittu.\n";
			} else {
				omat.insert(luku);
			}
		} else if (std::cin.fail()) {
			std::cin.clear();
			std::cin.ignore(1);
		} else {
			std::cout << "Syötteen luku ei onnistu, lopetetaan.\n";
			return 1;
		}
	}

mpni [21.02.2021 04:21:38]

#

Jos itse lähestyisin ongelmaa, niin valitsisin ohjelmointikieleksi Pythonin. Tämän jälkeen arpoisin 40 numeroa satunnaiseen järjestykseen ja tallentaisin numerot taulukkoon. Seuraavaksi arpoisin satunnaisesti indeksejä arvostusta taulukosta. Kun yksi indeksi on arvottu, niin sitä vastaava luku poistettaisiin taulukosta. Seuraavissa arvonnoissa huomioitaisiin vähennetyt indeksit ts. jokaisella seuraavalla kierroksella arvottavien indeksien lukumäärä olisi aina yhtä pienempi.

jalski [21.02.2021 15:16:31]

#

mpni kirjoitti:

(21.02.2021 04:21:38): Jos itse lähestyisin ongelmaa, niin valit­si­sin...

Miksei vaan suoraviivaisesti sekoittaisi taulukkoa missä on kaikki 40 numeroa ja ottaisi siitä 8 ( = 7 numeroa + 1 lisänumero ) numeron pituisen osan?


Sivun alkuun

Vastaus

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

Tietoa sivustosta