Gå til innhold

[Løst] Duplikathåndtering av integer i array eller lignende C#


Anbefalte innlegg

Heisann,



Sitter å fomler i mørket, uten noen åpenbart lys i sikt.

Intensjonen er å lage et verktøy som lar meg legge fram X tilfeldige runer på skjermen.

Det er totalt 24 forskjellige å velge mellom. Jeg tillater IKKE duplikater, altså jeg skal ikke ha samme runa 2 ganger.


Dette får jeg ikke til, har sett meg blind på koden og trenger nå friske øyne.

Har følgende metode

public void drawImage(int numRunes, int numBags)
	{
	List<int> usedInts = new List<int>();
	PictureBox[] boxes = {
	runeBox1, runeBox2, runeBox3, runeBox4, runeBox5, runeBox6, runeBox7, runeBox8, 
	runeBox9, runeBox10, runeBox11, runeBox12, runeBox13, runeBox14, runeBox15, runeBox16, 
	runeBox17, runeBox18, runeBox19, runeBox20, runeBox21, runeBox22, runeBox23, runeBox24, 
	runeBox25, runeBox26, runeBox27, runeBox28, runeBox29, runeBox30, runeBox31, runeBox32, 
	runeBox33, runeBox34, runeBox35, runeBox36, runeBox37, runeBox38, runeBox39, runeBox40, 
	runeBox41, runeBox42, runeBox43, runeBox44, runeBox45, runeBox46, runeBox47, runeBox48
	};
	int[] possibleRunes = Enumerable.Range(0, 23).ToArray();
	int sharedCounter = 0;
	Random rnd = new Random();
	if ( numBags == 1 ) // less than 2 bags? That means you cant have dupes!
		{
		while ( sharedCounter < numRunes )
			{
			int selected = rnd.Next(0, 23);
			if ( !checkDupe(selected, numRunes, usedInts) )
				{
				boxes[sharedCounter].ImageLocation = files[selected];
				sharedCounter++;
				}
			else
				{
				sharedCounter--; //decrement the counter i, so that it tries again.
				}
			}

		}
			else //he has more than one bag, dupes are ok.
			{
			while ( sharedCounter < numRunes )
				{
				int selected = rnd.Next(0, 23);
				boxes[sharedCounter].ImageLocation = files[selected];
				sharedCounter++;
				}
			}
		} 

Inne i en IF statement kaller jeg på metoded som skal sjekke duplikater, og deretter returnere en bool som sier om jeg skal prøve igjen eller tegne runen.

private bool checkDupe(int numToCheck, int tests, List<int> used)
	{
		for ( int i = 0; i < tests; i++ )
			{
			if ( !used.Contains(numToCheck) )
				{
				System.Console.WriteLine("Not a duplicate, returned false");
				used.Add(numToCheck);
				return false;
				}
			else
				{
				System.Console.WriteLine("Duplicate, returned true");
				return true;
				}
			}
		//defaults to false, as a fallback
		return false;
	}

Dette er nå siste versjon av duplikatsjekkern.. hvor er det jeg er dum (i henhold til duplikatsjekken)

Er relativt sikker på at koden er horibelt uoptimalisert, og har en bug inni dær som gjør at den kræsjer i blant under tegning av runen.

Lenke til kommentar
Videoannonse
Annonse

Den enkleste måte å gjøre det på er å ta ut elementene som du allerede har valgt fra lista. En dum konsekvens blir at selve lista ødelegges hvis du ikke tar en kopi først.

 

Edit: Jeg misforsto kanskje litt, men du skjønner tenkemåten.

 

Her er en algoritme som er ganske fiffig, og der man bevarer inneholdet (men ikke rekkefølgen) på lista over ting. JavaScript:


var list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17];//, 18, 19, 20, 21, 22, 23, 24];

var selectUnique = function(list, num) {
    var result = [];
    var select;
    var tmp;
    for (var i=0; i<num; ++i) {
        
        // Select an element, but not the i elements near the end of the array, because
        // this is where we put already selected numbers.
        select = floor(random(0, list.length-i));
        result.push(list[select]);
        
        // Remove element from selectable part of list and place it near the end
//        println(select + " : " + list[select]);
        
        tmp = list[select];
        list[select] = list[list.length-i-1];
        list[list.length-i-1] = tmp;
        
        //println(result);
        //println(list);
        //println("--");
    }
    //println("result: " + result);
    return result;
};


//println(
selectUnique(list, 6);

Endret av Emancipate
Lenke til kommentar

Hmm, må lese litt på det der orderby opplegget, er enda mye jeg ikke har kontroll på innenfor c#

 

Faktisk er det lite jeg faktisk har kontroll på. Men sånn er det når man er fersk.

 

 

Kjapt oppsummert så er teorien bak hele systemet dette:

 

  • 24 runer, listet opp fra 0 til 23.

 

Jeg vil ha 8 runer!

 

  • random tall mellom 0 og 23.
    • ​tegn runen på skjermen
    • putt tallet i en beholder
  • Velg nytt tall mellom 0 og 23
    • dersom tallet som ble valgt er allerede i beholderen, prøv på nytt!
    • gjennta til man når 8 unike resultater.

Men dette får jeg ikke til i koden.

 

Beklager om dette ikke kom helt klart frem i førsteposten.

Lenke til kommentar

Velg ut alle de random tallene først, deretter tegner du runene ut i fra det. Én ting om gangen.

 

 

Random X ganger -> array -> sorter array -> se etter tall som gjentar seg -> om duplikater slett og start på nytt?

 

 

sånn for eksempel?

for ( int i = 0; i < numRunes; i++ )
	{
		numGen[i] = rnd.Next(0, 23); // x runes
	}

Array.Sort(numGen);
Endret av Dwagon
Lenke til kommentar

Jeg tror du misforsto. Idéen om at du skal velge ut runer og tegne dem samtidig er bare feil. Det jeg mente var at du bør dele opp koden i to deler. En del som lager en liste over hvilke runer som skal tegnes, og en del som tegner dem. Dette fordi du da deler oppgaven i to mindre deler, der hver del er enklere å løse.

 

For å lage en liste over hvilke runer som skal tegnes kan du bruke algoritmen min eller lage din egen. IMO er det bare tull å se etter duplikater. Vil du ikke ha duplikater så skal du ikke legge til duplikater i utgangspunktet. Har du kommet dit at du ser etter duplikater har du gjort noe feil.

 

Koden din er dessuten helt uleselig fordi den } som lukker et statement ikke kommer på linja der statementet begynner.

 

if (sånn) {
    ja();
} else {
    ok();
}
if(sånn)
{
    ja();
}
if (sånn)
        {
        Nei!!! Ikke sånn.
        }
Og definitivt ikke sånn:

	if ( numBags == 1 ) // less than 2 bags? That means you cant have dupes!
		{
		}
			else //he has more than one bag, dupes are ok.
			{

			}
Endret av Emancipate
  • Liker 1
Lenke til kommentar

pseudokode:

int n = 8;
int index;
int[] chosenRunes = new Array<int>;
int[] possibleRunes = Enumerable.Range(0, 23).ToArray();
for (i=0; i<n; ++i) {
    index = rnd.Next(0, 23-i);
    chosenRunes.push(i);
    possibleRunes.delete_element(i);
}
Da har du tall i chosenRunes som du bruker når du skal tegne runene.
  • Liker 1
Lenke til kommentar

Akkurat, ja kan ikke skryte på meg at dette er noe jeg kan. Det er åpenbart.

Copy pasten inn fra visual studio er skitten.

Jeg kan rett og slett ikke nok om syntaxen til å løse det på en ren måte (enda!)

Så mens du skrev posten din fiklet jeg med kode og kom fram til dette.

private int[] GenerateRunes(int numRunes)
	{
	
	int[] numGen = new int[numRunes];

		for ( int i = 0; i < numRunes; i++ )
			{
			numGen[i] = rnd.Next(0, 23); // x runes
			}
	// time to sort.
	Array.Sort(numGen);
	// now throw into dupe checker
	if ( checkDupe(numGen) )
		{
		return GenerateRunes(numRunes); //woot internal method call!
		}
	else return numGen;
	}

public void drawImage(int numRunes, int numBags)
	{
		List<int> usedInts = new List<int>();
		PictureBox[] boxes = {MASSE RUNEBOKSER!};
		
		int[] runes = new int[numRunes];
		runes = GenerateRunes(numRunes); //make the runelist

		for ( int i = 0; i < runes.Length; i++ )
			{
				boxes[i].ImageLocation = files[runes[i]];
			}	
	}
	
private bool checkDupe(int[] array)
	{
		if ( array.Length < 2 )
			{
			return false;
			}

		for ( int i = 0; i < array.Length; i++ )
			{
			for ( int j = i+1; j < array.Length; j++ )
			{
				if ( array[i] == array[j] )
					{
					return true; //we found a dupe
					}
				}
			}
		return false; // all good, no dupes.
	}
	

Nå må jeg bare finne ut hvorfor jeg får stackoverflow når jeg ber om mer enn 18 runer.

Endret av Dwagon
Lenke til kommentar

Funksjonen din GenerateRunes er bare feil. :) Lag den sånn som jeg viste i innlegget over: en liste med alle tallene du kan velge fra, og en tom liste. Så velger du ut et tilfeldig tall fra liste 1, legger det til i liste 2 og sletter det fra liste 1. Dette gjør du 8 ganger (hvis du skal ha 8 tall).

 

Edit: Jeg hadde sikkert vært mer hjelpsom om jeg hadde kunnet C#.

Endret av Emancipate
Lenke til kommentar

I for loopen din skriver du

for (i=0; i<n; ++i) {
    index = rnd.Next(0, 23-i);
    chosenRunes.push(i);
    possibleRunes.delete_element(i);
}

Kan det være at du mente at index skal være det tallet jeg henter og sletter på?

I så fall må jeg endre fra array til list, ellers blir det uhm.... merkelig.

 

Jeg prøver litt kode og ser hvordan det går.

 

Setter pris på all hjelp, selv om du kanskje ikke kan C# :)

Lenke til kommentar
		private int[] GenerateRunes(int numRunes)
			{
			ArrayList possibleRunes = new ArrayList();
			Queue q = new Queue(Enumerable.Range(0, 23).ToArray());
			
			possibleRunes.AddRange(q);

			ArrayList numGen = new ArrayList();
			
			int index = 0;
			for ( int i = 0; i < numRunes; i++ )
				{
					index = rnd.Next(0, 23 - i);
					numGen.Add(i);
					possibleRunes.RemoveAt(i);
				}
			int[] runelist =  numGen.ToArray(typeof(int)) as int[];
			return runelist;

Sånn ser den ut nå. fungerer fint, men får feil når jeg passerer 12 runer. I motsetning til 18 fra den gamle metoden.

 

:hmm:

 

Skjønner ikke helt hensikten med indexen.

Endret av Dwagon
Lenke til kommentar

Her har du en enkel løsning:

List<PictureBox> randomRunes = yourRunes.OrderBy(i=>rnd.Next()).Take(8).ToList();

class SimpleRandomSelection
{
  static void Main()
  {
    List<int> yourRunes = new List<int>();         //your list of runes
    for (int i=0; i<24; i++) { yourRunes.Add(i); } //fill the list with something
    Random rnd = new Random();                     //instantiate your random source

    /// This is where the magic happens, a onliner that solves your problem:
    List<int> randomRunes = yourRunes.OrderBy(i=>rnd.Next()).Take(8).ToList();

    //print to console to show selection
    randomRunes.ForEach(i=>Console.WriteLine(i));
  }
}
Endret av Enthroner
  • Liker 1
Lenke til kommentar

 

Her har du en enkel løsning:

List<PictureBox> randomRunes = yourRunes.OrderBy(i=>rnd.Next()).Take(8).ToList();

class SimpleRandomSelection
{
  static void Main()
  {
    List<int> yourRunes = new List<int>();         //your list of runes
    for (int i=0; i<24; i++) { yourRunes.Add(i); } //fill the list with something
    Random rnd = new Random();                     //instantiate your random source

    /// This is where the magic happens, a onliner that solves your problem:
    List<int> randomRunes = yourRunes.OrderBy(i=>rnd.Next()).Take(8).ToList();

    //print to console to show selection
    randomRunes.ForEach(i=>Console.WriteLine(i));
  }
}

Epic, man lærer alltid noe nyttig.

 

takket være deg er nå verktøyet straks klart!

 

Var litt fomling da jeg møtte på en uvented kræsj.Men det viste seg å være feil i runelesningen fra fil samt en +1 error i en loop.

 

Men nå fungerer det som bare faen!

 

Alt som gjennstår nå er å legge til inputlogikk og begrensninger ihht bruk.

 

Tusen takk for all hjelpen.

 

Ja, kanskje greit å se på resultatet.

 

http://www.kraksletta.com/fotn/Runegenerator.html

Endret av Dwagon
  • Liker 1
Lenke til kommentar

Opprett en konto eller logg inn for å kommentere

Du må være et medlem for å kunne skrive en kommentar

Opprett konto

Det er enkelt å melde seg inn for å starte en ny konto!

Start en konto

Logg inn

Har du allerede en konto? Logg inn her.

Logg inn nå
  • Hvem er aktive   0 medlemmer

    • Ingen innloggede medlemmer aktive
×
×
  • Opprett ny...