Znajdowanie przywódcy ciągu

0

Witam,
mam za zadanie napisać program w c#, który będzie znajdować przywódcę II rzędu (każdy z nich powinien występować więcej niż 33% razy), poniżej jest przykład znajdowania przywódcy I rzędu, czy ma ktoś pomył jak przerobić ten kod żeby znajdował przywódcę II rzędu, ewentualnie na algorytm, który pozwoli na rozwiązanie zadania? Będę bardzo wdzięczna za pomoc :)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PD
{
    class Program
    {
        static void Main(string[] args)
        {
            var lista = new List<Int32> { 1, 1, 1, 1, 1, 2, 2, 3 };
            // znajdujemy element wystepujacy najczesciej
            var licznik = 0;
            var przywodca = lista[0];

            foreach (var element in lista)
            {
                if (element == przywodca)
                {
                    licznik += 1;
                }
                else
                {
                    if (licznik == 0)
                    {
                        przywodca = element;
                        licznik = 1;
                    }
                    else
                    {
                        licznik -= 1;
                    }
                }
            }

            Console.WriteLine(przywodca);
            Console.ReadKey();
        }
    }
}

0

Przywódca ciągu to element, który występuje w liście więcej razy niż połowa jej długości (jeśli połowa długości to, np., 3.5, to 4), a nie występujacy najczęściej, więc raczej będzie cięzko to przerobić.

1

a jakiś pomysł na sam algorytm?

A nie, dało by się przerobić ten, jeśli element występujacy najczęściej występuje więcej niż długość przez dwa, to mamy przywódcę; przywódca drugiego rzędu, więcej niż 33%, easy. Ale powstaje pytanie, co gdy istnieje przywódca pierwszego i drugiego rzędu; który ma być wybrany? Bo przywódca pierwszego rzędu jest też przywódcą drugiego.

2

Nie słyszałem o czymś takim jak "przywódca ciągu". Ale ja się na algorytmach dobrze nie znam, może dlatego. W każdym razie znalazłem coś takiego; cytat:

Przywódcą ciągu jest element, który występuje w ciągu więcej razy niż połowa długości tego ciągu.

Czyli uwzględniając to, co napisałaś, to dla ciągu o długości n:

  1. "przywódcą I rzędu" jest element występujący więcej niż n/2 razy; inaczej mówiąc, jeśli liczbę wystąpień "przywódcy I rzędu" oznaczymy przez k1, to k1 = n/2;
  2. "przywódcą II rzędu" jest element występujący więcej niż n/3 razy; inaczej mówiąc, jeśli liczbę wystąpień "przywódcy II rzędu" oznaczymy przez k2, to k2 = n/3.

Nie jestem pewien, czy "przywódca II rzędu" może być także "przywódcą I rzędu". Zakładam, że nie. Czyli że spełniony musi być dodatkowy warunek dla "przywódcy II rzędu": występuje mniej niż lub dokładnie n/2 razy.

W związku z tym pseudokod rekurencyjny mógłby tak wyglądać:

find_counts (arr, cur, res)
    if equal(cur, length(arr))
    then return res
    else
        elem <- find_index(res, x => equal(x[0], arr[cur]))
        if equal(elem, NULL)
        then // Nie znaleziono elementu; czyli - jest on nowy
            return find_counts(arr, cur + 1, concat(res, [arr[cur], 1]))
        else // Znaleziono element; czyli - zwiększ jego częstość występowania
            return find_counts(
                arr,
                cur + 1,
                concat(
                    subarr(res, elem - 1),
                    [res[elem][0], res[elem][1] + 1],
                    subarr(elem + 1, res)
                )
            )

find_all_second_leaders (arr, size, cur, res)
    if equal(cur, length(arr))
    then return res
    else
        if greater(arr[cur][1], size/3) and lessOrEqual(arr[cur][1], size/2)
        then // Przywódca II rzędu
            return find_all_second_leaders(arr, size, cur + 1, concat(res, arr[cur][0]))
        else // Idźmy dalej - to nie przywódca II rzędu
            return find_all_second_leaders(arr, size, cur + 1, res)

Żeby nie było, że nie przetestowałem, to przetestowałem. Nadal to tylko 1 test, więc mogą być błędy w tym algorytmie. O tak to wygląda w JavaScripcie:

const find_counts
	= (arr, cur, res) => {
		if (cur === arr.length) {
			return res;
		} else {
			const elem = res.findIndex(x => x[0] === arr[cur]);
			return elem === -1
				? find_counts(arr, cur + 1, res.concat([[arr[cur], 1]]))
				: find_counts(arr, cur + 1, [...res.slice(0, elem), [res[elem][0], res[elem][1] + 1], ...res.slice(elem + 1, res.length)]);
		}
	};
	
const find_all_second_leaders
	= (arr, size, cur, res) => {
		if (cur === arr.length) {
			return res;
		} else {
			if (arr[cur][1] > size/3 && arr[cur][1] <= size/2) {
				return find_all_second_leaders(arr, size, cur + 1, res.concat(arr[cur][0]));
			} else {
				return find_all_second_leaders(arr, size, cur + 1, res);
			}
		}
	};
	
const arr = [1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3];

const counts = find_counts(arr, 0, []);
console.log('counts:',counts.map(x => `(${x[0]}, ${x[1]})`)); // Wypisuje: counts: (1, 1),(2, 5),(3, 8)

const leaders = find_all_second_leaders(counts, arr.length, 0, []);
console.log('leaders:',leaders); // Wypisuje: leaders: 2

UPDATE: Poprawiłem niepoprawne counts w pseudokodzie find_all_second_leaders na poprawne arr.


UPDATE2: Spróbowałem trochę poprawić czytelność pseukodu rozbijając ostatnie rekurencyjne wywołanie funkcji find_counts na kilka linii (przedtem było w jednej).


UPDATE3: Zmieniłem nazwę null na NULL, żeby bardziej się odróżniała od nazw zmiennych (NULL to jednak stała – jeśli w ogóle tak klasyfikować – a nie zmienna).

0

Nie jestem pewien, czy "przywódca II rzędu" może być także "przywódcą I rzędu". Zakładam, że nie. Czyli że spełniony musi być dodatkowy warunek dla "przywódcy II rzędu": występuje mniej niż lub dokładnie n/2 razy.

Dlatego się zapytałem OPa, bo z definicji przywódca pierwszego rzędu, jest też przywódcą drugiego. I co zwracać gdy sa oba.
Co do pseudokodu, czemu rekurencyjnie? Nazwa find_counts, co to zwraca, przywódcę pierwszego rzędu?

0
  1. Rekurencyjnie, bo tak lubię. Pomyślałem, że skoro @kasiaspoko nie podała wymagania, że nie można rekurencyjnie, to czemu nie.
  2. Funkcja find_counts znajduje liczby wystąpień elementów unikalnych.

PS. Trochę żałuję, że nie udało mi się w jednej funkcji tego zmieścić. (Pewnie by się dało, ale pewnie byłoby tak samo podobnie, jak zrobić w jednej funkcji rekurencyjnej sortowanie szybkie – straszny misz-masz by wyszedł). Przez to kod jest dłuższy, a więc i więcej błędów mogło się wkraść. :/


PS2.

O, ten fragment (w pseudokodzie) mi nie pasuje – brzydki jest, podatny na błędy:

return find_counts(arr, cur + 1, concat(subarr(res, elem - 1), [res[elem][0], res[elem][1] + 1], subarr(elem + 1, res)))

Ale nie wiem, jak to ładniej zapisać.

0

Wydaje mi sie, że najbardziej zrozumiałe jest znajdowanie najczęstszego elementu, przy użyciu Dictionary, pseudokod.

function find_most_frequent(xs):
  d = dictionary
  for e in xs:
    if e not in d:
      d[e] = 1
    else:
      d[e] += 1
  # dla xs = [1, 1, 2]:
  print(d)  # -> {1: 2, 2: 1}

Pozostaje przestudiować metody słownika w C#, żeby wyciągnąć klucz o najwiekszej wartości.

4

źródło/inspiracja problemu
http://smurf.mimuw.edu.pl/node/287

Przywódca 1-go rzędu - więcej niż 50%
Może być tylko 1 albo wcale

Przywódca 2-go rzędu - więcej niż 33%
Może być 1, może być dwóch. Nie bardzo czuję co gdy jest dwóch.

Zrobić mapę częstotliwości wystąpień - O(n)
Znaleźć pierwszy klucz dla którego częstotliwość >33% - O(n)
Jeżeli jest też klucz z częstotliwością > 50% to go pominąć, brać tylko ten drugi.

O(n) + O(n) = O(n)

Można jeszcze to uprościć i na bieżąco sprawdzać, czy ka ndydat na przywódcę nie osiągnął progu 33% + 1 wystąpienie, pod warunkiem, że wtedy nas nie interesuje, że miał potencjał na >50%. Osiągnął >33% i w tym momencie "na własne życzenie kończy grę o milion, idzie do domu z nagrodą pół miliona".

3

https://people.eecs.berkeley.edu/~minilek/publications/papers/bptreev2.pdf

"Cormen + heavy hitter" -> Google mówi, żeby się nie martwić, bo zaliczy się ASD bez tego tematu. ;)
Profesor Cormen nie przewiduje odpytywać z tego ani męczyć tym na wykładach.

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