Strona główna » Algorytmy » Artykuły » Unikalne Pary
 

Unikalne Pary

Zadanie

Dana jest lista liczb całkowitych dla której trzeba znaleźć ile unikalnych par można z nich uzyskać. Zakładamy, że kolejność liczb w parze ma znaczenie, a więc (a, b) jest różne od (b, a).

Przykład

Przypuśćmy, że dana jest tablica {1, 2, 1}. Możliwe unikalne pary do uzyskania to {(1, 1), (1, 2), (2, 1), (2, 2)}, a więc program powinien zwrócić 4 tj. liczbę uzyskanych różnych par liczb.

Implementacja

Rozwiązanie Siłowe

Najprostsze rozwiązanie polega na przejrzeniu wszystkich możliwych par i wrzucanie ich do zbioru. Na koniec zwracany jest rozmiar zbioru, który z definicji nie posiada dwóch identycznych wartości. Charakteryzuje je kwadratowa złożoność czasowa i pamięciowa O(n2). Oznacza to, że algorytm będzie działał długo i zużywał dużo pamięci. Oto przykładowy kod:

  1. static int IleUnikalnychPar(List<int> lista)
  2. {
  3. HashSet<Tuple<int, int>> zbior = new HashSet<Tuple<int, int>>();
  4. foreach(int i in lista)
  5. {
  6. foreach(int j in lista)
  7. {
  8. zbior.Add(new Tuple<int, int>(i, j));
  9. }
  10. }
  11. return zbior.Count;
  12. }

Na początku deklarowany jest zbiór par, a następnie w podwójnej pętli generowane są wszystkie możliwe pary, a na koniec zwracany jest rozmiar uzyskanego zbioru.

Rozwiązanie Optymalne

Poprzedni algorytm można poprawić ze względu na czas działania jak i na zużycie pamięci. Po pierwsze warto zauważyć, że dla n unikalnych wartości istnieje dokładnie n2 par. W celu wyznaczenia unikalnych wartości sortujemy dane, a potem zliczamy ile jest unikalnych wartości. Wynikiem działania funkcji będzie kwadrat licznika. Złożoność czasowa takiego algorytmu zależy od wybranego algorytmu sortowania np. O(n·logn). Złożoność pamięciowa może być O(1) jeśli algorytm będzie sortować w miejscu. To znacznie lepszy wynik niż w rozwiązaniu siłowym.

  1. static int IleUnikalnychPar(int[] lista)
  2. {
  3. Array.Sort(lista);
  4. int poprz = lista[0];
  5. int unikalnych = 1;
  6. for (int i = 1; i < lista.Length; i++)
  7. {
  8. if (lista[i] != poprz)
  9. {
  10. unikalnych++;
  11. poprz = lista[i];
  12. }
  13. }
  14. return unikalnych * unikalnych;
  15. }

Algorytm sortuje dane, a następnie inicjalizuje licznik na wartość 1 i porównuje kolejne pary elementów w posortowanej tablicy. Jeśli wartości są różne to licznik zostaje zwiększony. Na koniec zwracany jest kwadrat obliczonego licznika.

Testowanie funkcji

W celu przetestowania kodu można skorzystać z poniższego fragmentu programu, który wczyta od użytkownika listę liczb, a następnie wypisze ile unikalnych liczb można uzyskać.

  1. static void Main(string[] args)
  2. {
  3. Console.WriteLine("Podaj liczby:");
  4. List<int> lista = new List<int>();
  5. string[] dane = Console.ReadLine().Split(' ');
  6. for(int i = 0; i < dane.Length; i++)
  7. {
  8. lista.Add(Convert.ToInt32(dane[i]));
  9. }
  10. int wynik = IleUnikalnychPar(lista);
  11. Console.WriteLine("Unikalnych par: {0}", wynik);
  12. Console.ReadKey();
  13. }