Sprawnie dostać posortowane sum posortowanej listy

głosy
17

Masz listę rosnąco numerów, co jest najbardziej skuteczny algorytm można myśleć, aby otrzymać listę rosnąco kwot każdych dwóch liczb w tym wykazie. Duplikaty w wynikowej liście są nieistotne, można je usunąć lub ich unikać, jeśli chcesz.

Żeby było jasne, jestem zainteresowany w algorytmie. Krępuj się pisać kod w dowolnym języku i paradygmatu, które lubisz.

Utwórz 03/08/2008 o 22:08
źródło użytkownik
W innych językach...                            


8 odpowiedzi

głosy
-4

Jeśli szukasz prawdziwie języka roztworu agnostyka wtedy będzie bardzo rozczarowany moim zdaniem, ponieważ będziesz tkwić w pętli for i niektórych warunkowych. Jeśli jednak otworzył je do języków funkcyjnych lub funkcjonalnych cech językowych (Czekam na ciebie LINQ) wtedy moi koledzy tutaj można wypełnić tę stronę i eleganckich przykładów w Ruby, Lisp, Erlang, i inne.

Odpowiedział 03/08/2008 o 22:24
źródło użytkownik

głosy
1

Najlepsze co mogłem wymyślić jest stworzenie macierzy sum każdej pary, a następnie scalić wiersze razem, a-la scalania sortowania. Czuję się jakbym brakuje jakiś prosty wgląd że ujawni wiele bardziej efektywne rozwiązanie.

Mój algorytm, w Haskell:

matrixOfSums list = [[a+b | b <- list, b >= a] | a <- list]

sortedSums = foldl merge [] matrixOfSums

--A normal merge, save that we remove duplicates
merge xs [] = xs
merge [] ys = ys
merge (x:xs) (y:ys) = case compare x y of
    LT -> x:(merge xs (y:ys))
    EQ -> x:(merge xs (dropWhile (==x) ys))
    GT -> y:(merge (x:xs) ys)

Znalazłem niewielką poprawę, taki, który jest bardziej podatny na leniwe kodowania strumienia oparte. Zamiast łączenia kolumn parami, łączyć je wszystkie naraz. Zaletą jest, że zaczniesz od razu się elementy listy.

-- wide-merge does a standard merge (ala merge-sort) across an arbitrary number of lists
-- wideNubMerge does this while eliminating duplicates
wideNubMerge :: Ord a => `a` -> [a]
wideNubMerge ls = wideNubMerge1 $ filter (/= []) ls
wideNubMerge1 [] = []
wideNubMerge1 ls = mini:(wideNubMerge rest)
    where mini = minimum $ map head ls
          rest = map (dropWhile (== mini)) ls

betterSortedSums = wideNubMerge matrixOfSums

Jednakże, jeśli wiesz, że będziesz korzystać ze wszystkich kwot, i nie ma przewagę do uzyskania niektórych z nich wcześniej, iść z „ foldl merge []”, jak to szybciej.

Odpowiedział 03/08/2008 o 22:36
źródło użytkownik

głosy
4

Zamiast kodowania to, ja będę figurować pseudo-kodu to w krokach i wyjaśnić moją logikę, tak że lepsze programiści mogą w razie potrzeby grzebać dziury w moim logiki.

Na pierwszym etapie zaczynamy z listą numerów długości n. Dla każdego numeru musimy utworzyć listę długości n-1 becuase nie dodajesz numer do siebie. Przy końcu posiada listę o N klasyfikowane listy, które zostało wytworzone w O (N ^ 2) czasu.

step 1 (startinglist) 
for each number num1 in startinglist
   for each number num2 in startinglist
      add num1 plus num2 into templist
   add templist to sumlist
return sumlist 

W kroku 2, ponieważ wykazy zostały posortowane według wzoru (dodać numer do każdego elementu na liście sortowane i lista nadal będą sortowane) możemy po prostu zrobić mergesort łącząc każdą listę razem zamiast mergesorting całe mnóstwo. Na koniec powinno to O (N ^ 2) czasu.

step 2 (sumlist) 
create an empty list mergedlist
for each list templist in sumlist
   set mergelist equal to: merge(mergedlist,templist)
return mergedlist

Sposób seryjnej byłoby wtedy normalny etap scalania z czekiem, aby upewnić się, że nie ma zduplikowane sum. Nie będę pisał, bo to każdy może patrzeć mergesort.

Więc jest moje rozwiązanie. Cały algorytm O (N ^ 2) czasu. Zapraszam wskazać ewentualne błędy lub ulepszeń.

Odpowiedział 04/08/2008 o 00:06
źródło użytkownik

głosy
1

W SQL:

create table numbers(n int not null)
insert into numbers(n) values(1),(1), (2), (2), (3), (4)


select distinct num1.n+num2.n sum2n
from numbers num1
inner join numbers num2 
    on num1.n<>num2.n
order by sum2n

C # LINQ:

List<int> num = new List<int>{ 1, 1, 2, 2, 3, 4};
var uNum = num.Distinct().ToList();
var sums=(from num1 in uNum
        from num2 in uNum 
        where num1!=num2
        select num1+num2).Distinct();
foreach (var s in sums)
{
    Console.WriteLine(s);
}
Odpowiedział 09/08/2008 o 00:05
źródło użytkownik

głosy
2

Można to zrobić w dwóch liniach w Pythonie z

allSums = set(a+b for a in X for b in X)
allSums = sorted(allSums)

Koszt tego jest n ^ 2 (może dodatkowy czynnik protokołu dla zestawu?) W log (i) powtórzenie a s * do sortowania, w którym s oznacza rozmiar zestawu.

Wielkość zestaw może być tak duża, jak n * (n-1) / 2, na przykład, gdy x = [1,2,4, ..., 2 ^ n]. Więc jeśli chcesz, aby wygenerować tę listę potrwa co najmniej n ^ 2/2, w najgorszym przypadku, ponieważ jest to wielkość produkcji.

Jednak jeśli chcesz, aby wybrać pierwsze elementy K wyniku Można to zrobić w czasie O (kN) przy użyciu algorytmu selekcji posortowane macierze X + Y przez Frederickson i Johnsona ( patrz tutaj krwawych szczegółów) . Chociaż może to być prawdopodobnie zmodyfikowany tak, aby generować je online poprzez ponowne obliczenie i uzyskać sprawny generator dla tego zestawu.

@deuseldorf Peter Istnieje pewne zamieszanie o (n!) Wątpię deuseldorf oznaczało „n czynnikowych”, ale po prostu „n, (bardzo podniecony)!”

Odpowiedział 11/08/2008 o 15:47
źródło użytkownik

głosy
1

Kwestia ta została wracking mój mózg przez około jeden dzień teraz. Niesamowite.

Tak czy inaczej, nie można uciec od n ^ 2 natury łatwo, ale można to zrobić nieco lepiej z seryjnej, ponieważ można związany zakres wstawić każdy element.

Jeśli spojrzeć na wszystkich listach generowanych, mają następującą postać:

(a[i], a[j]) | j>=i

Jeśli obrócić go o 90 stopni, można uzyskać:

(a[i], a[j]) | i<=j

Teraz proces scalania powinny przyjmować dwie listy ii i+1(które odpowiadają na listy, gdzie pierwszy człon jest zawsze a[i]a a[i+1]), można związany zakres do wstawienia elementu (a[i + 1], a[j])do listy iod lokalizacji (a[i], a[j])i lokalizacji (a[i + 1], a[j + 1]).

Oznacza to, że należy połączyć w odwrotnej kolejności w kategoriach j. Nie wiem (jeszcze), czy można wykorzystać to w poprzek j, a także, ale wydaje się to możliwe.

Odpowiedział 21/08/2008 o 19:16
źródło użytkownik

głosy
12

Edycja z 2018 roku, jak: Powinieneś prawdopodobnie zakończyć czytanie tego. (Ale nie mogę go usunąć, ponieważ jest akceptowana).

Jeśli pisać sum jak to:

1 4  5  6  8  9
---------------
2 5  6  7  9 10
  8  9 10 12 13
    10 11 13 14
       12 14 15
          16 17
             18

Zauważysz, że od M [i, j] <= M [i, j + 1] M [i, j] <= M [i + 1, j], a następnie trzeba tylko zbadać górny lewy " rogi”i wybierz najniższą jeden.

na przykład

  • tylko 1 lewym górnym rogu, wybierz 2
  • tylko 1, pick 5
  • 6 albo 8, wybrać 6
  • 7 albo 8, piłki 7
  • 9 lub 8, wybrać 8
  • 9 lub 9, podnieś obie :)
  • 10 lub 10 lub 10, odebrać wszystko
  • 12 albo 11, odbiór 11
  • 12 albo 12, wybrać obie
  • 13 lub 13, podnieś obie
  • 14 lub 14, podnieś obie
  • 15 lub 16, pick 15
  • tylko 1, pick 16
  • tylko 1, pick 17
  • tylko 1, pick 18

Oczywiście, gdy masz wiele z najlepszych lewych rogach wtedy to rozwiązanie nakładanych.

Jestem całkiem pewien problem ten jest Ω (n²), bo trzeba obliczyć sumy za każdy m [i, j] - chyba że ktoś ma lepszy algorytm sumowania :)

Odpowiedział 18/09/2008 o 22:41
źródło użytkownik

głosy
1

Bez względu na to, co robisz, bez dodatkowych ograniczeń na wartości wejściowych, nie można zrobić lepiej niż O (n ^ 2), po prostu dlatego, że trzeba wykonać iterację wszystkich par liczb. Iteracja będzie dominować sortowania (które można zrobić w czasie O (n log n) lub szybszy).

Odpowiedział 18/09/2008 o 23:15
źródło użytkownik

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more