losowanie bez powtórzeń 2D

0

Witam.

Jestem tutaj nowy i nie wiem czy we właściwym dziale dałem temat. Jest on bardziej algorytmiczny.
Chodzi mi o losowanie punktów 2D z zadanej przestrzeni tak, aby się nie powtarzały aż do wyczerpania puli.

Oto, co napisałem w Javie:

import java.util.*;

/**
 * Losowanie bez zwracania punktu z przestrzeni 2D.
 */
 
public class UniquePoint2DGenerator {
	private final List<Integer> xCoords = new LinkedList<Integer>();
	private final List<List<Integer>> yCoords = new LinkedList<List<Integer>>();
	private final List<Integer> indexes = new LinkedList<Integer>();
	private final int X_MIN, Y_MIN, X_MAX, Y_MAX, MAX_POINTS;
	private final Random rand = new Random();
	private int counter;
	
	/**
	 * @param xMax rozmiar przestrzeni 2D w kierunku X
	 * @param yMax rozmiar przestrzeni 2D w kierunku Y
	 */
	public UniquePoint2DGenerator(int xMin, int yMin, int xMax, int yMax){
		X_MIN = xMin;
		Y_MIN = yMin;
		X_MAX = xMax;
		Y_MAX = yMax;
		MAX_POINTS = (X_MAX - X_MIN) * (Y_MAX - Y_MIN);
		counter = MAX_POINTS;
		initializeCoords();
	}
	
	private void initializeCoords(){
		for(int i=X_MIN, k=0; i<X_MAX; i++, k++){
			xCoords.add(i);
			indexes.add(k);
			
			List<Integer> tmp = new LinkedList<Integer>();
			
			for(int j=Y_MIN; j<Y_MAX; j++){
				tmp.add(j);
			}
			
			yCoords.add(tmp);
		}
	}
	
	private void removeEmptyList(){
		for(int i=0; i<indexes.size(); i++){
			int index = indexes.get(i);
			if(yCoords.get(index).isEmpty()){
				indexes.remove(i);
				return;
			}
		}
	}
	
	/**
	 * @return unikalny punkt z przestrzeni 2D lub null jeśli pula
	 * punktów została wyczerpana. Resetowanie generatora następuje
	 * po wywołaniu metody reset()
	 */
	public Point nextPoint(){
		if(counter == 0){
			return null;
		}
		counter--;
		
		removeEmptyList();
		
		int indexPos = rand.nextInt(indexes.size());
		int xPos = indexes.get(indexPos);
		int x = xCoords.get(xPos);
		int yPos = rand.nextInt(yCoords.get(xPos).size());
		int y = yCoords.get(xPos).remove(yPos);
		
		return new Point(x, y);
	}
	
	/**
	 * Resetowanie generatora. Po wywołaniu metoda nextPoint() ponownie
	 * będzie zwracała punkty zamiast wartości null.
	 * 
	 * 
	 */
	
	public void reset(){
		xCoords.clear();
		yCoords.clear();
		indexes.clear();
		initializeCoords();
		counter = MAX_POINTS;
	}
	
	//test case
	public static void main(String... args){
		
		Matrix m = new Matrix();
		m.print();
		System.out.println();
		UniquePoint2DGenerator rand = new UniquePoint2DGenerator(10,9,19,18);
		Point p;
		int a = 0;
		while( (p = rand.nextPoint()) != null ){
			if(a%2 == 0)m.put(p);
			a++;
		}
		m.print();
		
	}
}

class Matrix{
	char[][] tab = new char[20][20];
	
	public Matrix(){
		for(int i=0; i<20; i++){
			for(int j=0; j<20; j++){
				tab[i][j]='o';
			}
		}
	}
	public void print(){
		for(int i=0; i<20; i++){
			for(int j=0; j<20; j++){
				System.out.print(tab[i][j] + " ");
			}
			System.out.println();
		}
	}
	
	public void put(Point p){
		tab[p.getX()][p.getY()] = 'x';
	}
}

Czy da się to napisać ładniej, wydajniej?

Metoda ta będzie krytyczna dla aplikacji.

Z góry dziękuję za wskazówki i sugestie!

0

Będzie ktoś tak dobry i przeanalizuje kod?

0

Losowanie unikalnych punktów 2D mozna zrobic np tak:

  • losujesz wspolrzedne i sprawdzasz czy w jakiejś mapie (np HashMap) nie znajduje sie przypadkiem klucz reprezentujacy takie same wsporzedne
  • jesli sie NIE znajduje, to wsadzasz ten punkt do mapy jako nowy klucz
  • jesli jest tam, to losujesz ponownie
  • operacja przebiega do czasu, az liczba elementow w mapie bedzie rowna ilosci punktow, ktore chcesz wylosowac
0

Na początek przyznam się, że nie zagłębiałem się w Twój kod shooter3, nie chciało mi się analizować ;), odpowiem tylko na @up, bo jeżeli chcesz zastosować algorytm z niego, myślę, że może być nie-fajnie.
Jeżeli chodzi o algorytm, to trochę inaczej bym to zrobił, aby uniknąć

[losowa nazwa] napisał(a)

(...)

  • jesli jest tam, to losujesz ponownie
  • operacja przebiega do czasu, az liczba elementow w mapie bedzie rowna ilosci punktow, ktore chcesz wylosowac
    w ten sposób czas działania programu, jest zróżnicowany zależnie od indywidualnej sesji (czasami szybciej trafi w odpowiednie punkty, czasami wolniej).
    Ominąłbym to w taki sposób:
  • stworzyłbym tablicę zawierającą wszystkie możliwe punkty ('po kolei') oraz zapisującą ich stan (0/1 - nie użyty/użyty),
  • następnie losowałbym index elementu tablicy (punktu), sprawdzał czy jego status jest ==0, jeżeli tak:
    • przepisywałbym go do nowej tablicy (w kolejności wylosowań) i zmieniał status w poprzedniej tablicy z 0 na 1
      jeżeli nie:
    • dodawałbym do wylosowanego indexu+1 aż do końca tabeli i przeprowadzał powyższe jeszcze raz, aż trafi na stan 0 (jeżeli tabela się skończy - zaczynamy od elementu 0)</span>
  • operacja trwa, do zapełnienia tablicy wynikowej (możesz już na początku określić jej rozmiar - równy rozmiarowi pierwszej tablicy)
  • tablica wynikowa (ta druga, do której 'kopiowaliśmy' wylosowane punkty), byłaby losowa - odtwarzając ją w 'normalnej' kolejności, otrzymalibyśmy efekt randoma.

Na czerwono zaznaczyłem część algorytmu, która zastępuje tamten 'losowy fragment'. Przy małej liczbie punktów (powiedzmy, z 10), może szybciej byłoby programowi 'wstrzelić się' w ostatni punkt, gdy 9 byłoby już wylosowanych, jednak gdy działałby na liczbach rzędu 1000+, myślę, że mój algorytm byłby wydajniejszy (trafić w 1 liczbę gdy 999 jest już wylosowanych... do tego dodaj, każde kolejne wylosowanie, gdy już chociażby z 950 jest wylosowanych - nie wiadomo ile by to trwało). Na pewno byłoby stabilniej.

Pozdro

0

istnieje prostszy i bardziej wydajny sposób niewymagający szukania już wylosowanych punktów:

  1. Tworzymy tablicę T z wszystkimi elementami do wylosowania i ustawiamy zmienną N na długość tablicy.
  2. Losujemy indeks I z przedziału 0 - N-1.
  3. Element T[I] to aktualnie wylosowany element.
  4. Zamieniamy element T[N - 1] z T[I]
  5. Zmniejszamy N o jeden.
  6. Wracamy do punktu 2.

W ten sposób przejdziemy przez wszystkie możliwe punkty tylko raz.

0

Tak, zdecydowanie lemmiwink przedstawił lepsze rozwiązanie.

0

Dzięki koledzy, a w szczególności lemmiwink.

Jutro refaktoryzacja kodu.

0
lemmiwink napisał(a)
  1. Tworzymy tablicę T z wszystkimi elementami do wylosowania i ustawiamy zmienną N na długość tablicy.
  2. Losujemy indeks I z przedziału 0 - N-1.
  3. Element T[I] to aktualnie wylosowany element.
  4. Zamieniamy element T[N - 1] z T[I]
  5. Zmniejszamy N o jeden.
  6. Wracamy do punktu 2.

Kroki 2-6 mozna sprowadzic do wywolania java.util.Collections.shuffle(List list).

1 użytkowników online, w tym zalogowanych: 0, gości: 1