Efficiënt krijgen gesorteerde sommen van een gesorteerde lijst

stemmen
17

Je hebt een oplopende lijst getallen, wat is de meest efficiënte algoritme je kunt bedenken om de stijgende lijst van bedragen van om de twee getallen in die lijst te krijgen. Duplicaten in de resulterende lijst niet relevant zijn, kunt u ze verwijderen of te voorkomen dat ze als je wilt.

Voor alle duidelijkheid, ik ben geïnteresseerd in het algoritme. Voel je vrij om code te posten in elke taal en paradigma die je leuk vindt.

De vraag is gesteld op 03/08/2008 om 22:08
bron van user
In andere talen...                            


8 antwoorden

stemmen
-4

Als u op zoek bent naar een echte taal agnostisch oplossing dan zult u erg teleurgesteld in mijn mening, want je zult worden geplakt met een lus en een aantal voorwaardelijke. Maar als je het opengesteld voor functionele talen of functionele taalfuncties (Ik ben op zoek naar jou LINQ), dan mijn collega's hier kan deze pagina te vullen met elegante voorbeelden in Ruby, Lisp, Erlang, en anderen.

antwoordde op 03/08/2008 om 22:24
bron van user

stemmen
1

Het beste kon bedenken is een matrix van sommen van elk paar elkaar produceren, en vervolgens samenvoegen van de rijen a-la merge sort. Ik voel me alsof ik mis een aantal eenvoudige inzicht dat een veel efficiëntere oplossing zal onthullen.

Mijn algoritme, in 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)

Ik vond een kleine verbetering, een die meer vatbaar zijn voor lazy-stroom gebaseerde codering. In plaats van het samenvoegen van de kolommen paarsgewijze, samen te voegen ze allemaal tegelijk. Het voordeel is dat je begint steeds elementen van de lijst onmiddellijk.

-- 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

Echter, als je weet dat je gaat om alle bedragen te gebruiken, en er is geen voordeel aan het krijgen van een aantal van hen eerder te gaan met ' foldl merge []', want het is sneller.

antwoordde op 03/08/2008 om 22:36
bron van user

stemmen
4

In plaats van het coderen van dit uit, ik denk dat ik zal pseudo-code in stappen en uit te leggen mijn logica, zodat die beter programmeurs gaten in mijn logica kan steken indien nodig.

Op de eerste stap beginnen we met een lijst van nummers lengte n. Voor elk nummer moeten we een lijst met lengte n-1 creëren becuase we niet een aantal toe te voegen aan zichzelf. Aan het eind hebben we een lijst van ongeveer n gesorteerd lijsten die werd gegenereerd in O (n ^ 2) tijd.

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 

In stap 2, omdat de lijsten zijn gesorteerd op design (een nummer toevoegen aan elk element in een gesorteerde lijst en de lijst zal nog steeds worden opgelost) kunnen we gewoon een mergesort doen door het samenvoegen van elke lijst samen in plaats van mergesorting de hele boel. Uiteindelijk zou dit O (n ^ 2) tijd.

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

De samenvoeging methode zou dan de normale samenvoegen stap met een controle om ervoor te zorgen dat er geen dubbele bedragen. Ik zal niet schrijf dit omdat iedereen kan opzoeken mergesort.

Dus daar is mijn oplossing. Het gehele algoritme is O (n ^ 2) tijd. Voel je vrij om te wijzen op eventuele fouten of verbeteringen.

antwoordde op 04/08/2008 om 00:06
bron van user

stemmen
1

In 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);
}
antwoordde op 09/08/2008 om 00:05
bron van user

stemmen
2

U kunt dit doen in twee lijnen in python met

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

De kosten van deze is n ^ 2 (misschien een extra log factor voor de set?) Voor de iteratie en s * log (s) voor het sorteren waar s de grootte van de set.

De grootte van de verzameling kan zo groot als n * (n-1) / 2 bijvoorbeeld als X = [1,2,4, ..., n ^ 2] zijn. Dus als je wilt om deze lijst te genereren zal het op zijn minst n ^ 2/2 in het ergste geval, omdat dit is de grootte van de output.

Maar als je wilt de eerste k elementen van het resultaat dat u kunt dit doen in O (kn) te selecteren met behulp van een selectie algoritme voor het naargelang X + Y matrices door Frederickson en Johnson ( zie hier voor de bloederige details) . Hoewel dit waarschijnlijk kan worden gewijzigd om ze online te genereren door hergebruik van berekening en ontvang een efficiënte generator voor deze set.

@deuseldorf, Peter Er is enige verwarring over (n!) Ik betwijfel deuseldorf betekende "n faculteit", maar gewoon "n, (erg enthousiast)!"

antwoordde op 11/08/2008 om 15:47
bron van user

stemmen
1

Deze vraag is wracking mijn hersenen voor ongeveer een dag nu. Geweldig.

Anyways, kun je niet gemakkelijk weg van de n ^ 2 aard van het, maar je kunt iets beter met de samenvoeging doen, omdat u het bereik om elk element in te voegen kan worden gebonden.

Als je kijkt naar alle lijsten die u genereert, ze hebben de volgende vorm:

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

Als je te spiegelen 90 graden, krijg je:

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

Nu moet het merge proces te nemen van twee lijsten ien i+1(die overeenkomen met lijsten waarbij het eerste lid is altijd a[i]en a[i+1]), kunt u het bereik gebonden aan element in te voegen (a[i + 1], a[j])in de lijst idoor de locatie van (a[i], a[j])en de locatie van (a[i + 1], a[j + 1]).

Dit betekent dat je moet samenvoegen in omgekeerde volgorde in termen van j. Ik weet het niet (nog) niet als je dit over gebruik kunnen maken jals goed, maar het lijkt mogelijk.

antwoordde op 21/08/2008 om 19:16
bron van user

stemmen
12

Bewerken als 2018: U moet waarschijnlijk stoppen met het lezen van deze. (Maar ik kan het niet verwijderen als het wordt geaccepteerd.)

Als u schrijven de bedragen als volgt:

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

U zult merken dat, aangezien M [i, j] <= M [i, j + 1] en M [i, j] <= M [i + 1, j], dan moet je alleen naar linksboven "onderzoeken hoeken" en kiezen voor de laagste is.

bv

  • slechts 1 linkerbovenhoek, kies 2
  • slechts 1, pick 5
  • 6 of 8, pick 6
  • 7 of 8, 7 kies
  • 9 of 8, kies 8
  • 9 of 9, pak beide :)
  • 10 of 10 of 10, pak alle
  • 12 of 11, pick 11
  • 12 of 12, pick beide
  • 13 of 13, pick beide
  • 14 of 14, pick beide
  • 15 of 16, kies 15
  • slechts 1, pick 16
  • slechts 1 pick 17
  • slechts 1, pick 18

Natuurlijk, als je veel van linkerbovenhoeken dan is deze oplossing vererft.

Ik ben er vrij zeker van dat dit probleem is Ω (n²), want je moet de bedragen te berekenen voor elke M [i, j] - tenzij iemand heeft een betere algoritme voor de sommering :)

antwoordde op 18/09/2008 om 22:41
bron van user

stemmen
1

Het maakt niet uit wat je doet, zonder extra beperkingen op de input waarden, kan je niet beter doen dan O (n ^ 2), simpelweg omdat je moet doorlopen alle paren van getallen. De iteratie zal domineren sortering (wat je kunt doen in O (n log n) of sneller).

antwoordde op 18/09/2008 om 23:15
bron van user

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