Code golf: объединение нескольких отсортированных списков в один отсортированный список
Реализовать алгоритм для объединения произвольного количества отсортированных списков в один отсортированный список. Цель состоит в том, чтобы создать самую маленькую рабочую программу на любом языке, который вам нравится.
Например:
input: ((1, 4, 7), (2, 5, 8), (3, 6, 9))
output: (1, 2, 3, 4, 5, 6, 7, 8, 9)
input: ((1, 10), (), (2, 5, 6, 7))
output: (1, 2, 5, 6, 7, 10)
Примечание: решения, которые объединяют входные списки, а затем используют предоставленную языком функцию сортировки, не соответствуют духу гольфа и не принимаются:
sorted(sum(lists,[])) # cheating: out of bounds!
Помимо всего прочего, ваш алгоритм должен быть (но не обязательно) намного быстрее!
Четко укажите язык, любые недостатки и количество символов. Включите только значащие символы в счетчике, но не стесняйтесь добавлять пробелы в код для художественных/удобочитаемых целей.
Чтобы все было в порядке, предлагайте улучшение комментариев или редактируя ответы там, где это необходимо, вместо того, чтобы создавать новый ответ для каждой "ревизии".
РЕДАКТИРОВАТЬ: если бы я снова задавал этот вопрос, я бы добавил, что правило "не предоставлено языком" не будет "конкатенировать все списки, а затем сортировать результат". Существующие записи, которые делают concatenate-then-sort, на самом деле очень интересны и компактны, поэтому я не буду активно внедрять правило, которое они нарушают, но не стесняйтесь работать с более ограничительной спецификацией в новых представлениях.
Вдохновленный Сочетание двух отсортированных списков в Python
Ответы
Ответ 1
Common Lisp уже имеет функцию merge
для общих последовательностей в стандарте языка, но работает только в двух последовательностях. Для нескольких списков номеров, отсортированных по возрастанию, его можно использовать в следующей функции (97 основных символов).
(defun m (&rest s)
(if (not (cdr s))
(car s)
(apply #'m
(cons (merge 'list (car s) (cadr s) #'<)
(cddr s)))))
edit: Повторное редактирование через некоторое время: это можно сделать в одной строке:
(defun multi-merge (&rest lists)
(reduce (lambda (a b) (merge 'list a b #'<)) lists))
У этого есть 79 существенных персонажей со значимыми именами, уменьшая их до одной буквы, он выходит в 61:
(defun m(&rest l)(reduce(lambda(a b)(merge 'list a b #'<))l))
Ответ 2
OCaml в 42 символах:
let f=List.fold_left(List.merge compare)[]
Я думаю, что я должен получить дополнительный кредит за 42 точно?
Ответ 3
Python: 113 символов
def m(c,l):
try:
c += [l[min((m[0], i) for i,m in enumerate(l) if m)[1]].pop(0)]
return m(c,l)
except:
return c
# called as:
>>> m([], [[1,4], [2,6], [3,5]])
[1, 2, 3, 4, 5, 6]
EDIT:, поскольку разговоры о производительности появились в нескольких местах, я упомянул, что я считаю, что это довольно эффективная реализация, особенно по мере роста списков. Я выполнил три алгоритма на 10 списках отсортированных случайных чисел:
- мое решение (слияние)
-
sorted(sum(lists, []))
(встроенный)
- Deestan solution, которые отсортированы по-другому (Повторно реализовать)
EDIT 2: (JFS)
Знаки с цифрами:
-
merge_26
- heapq.merge()
из Python 2.6 stdlib
-
merge_alabaster
- приведенный выше код (помечен как Merge
на приведенном выше рисунке)
-
sort_builtin
- L = sum(lists,[]); L.sort()
- Deestan solution - O (N ** 2), поэтому он исключается из сравнения (все остальные решения - O (N) (для введенного ввода))
Входные данные [f(N) for _ in range(10)]
, где f()
:
max_ = 2**31-1
def f(N):
L = random.sample(xrange(max_), n)
L.sort()
return L
f.__name__ = "sorted_random_%d" % max_
ПРИМЕЧАНИЕ: merge_alabaster()
не работает для N > 100
из-за RuntimeError: "maximum recursion depth exceeded"
.
Чтобы получить скрипты Python, которые сгенерировали этот рисунок, введите:
$ git clone git://gist.github.com/51074.git
Заключение. Для достаточно больших списков встроенная сортировка показывает почти линейное поведение, и она является самой быстрой.
Ответ 4
Ruby: 100 символов (1 значительный пробел, 4 символа новой строки)
def m(i)
a=[]
i.each{|s|s.each{|n|a.insert((a.index(a.select{|j|j>n}.last)||-1)+1,n)}}
a.reverse
end
Человеческая версия:
def sorted_join(numbers)
sorted_numbers=[]
numbers.each do |sub_numbers|
sub_numbers.each do |number|
bigger_than_me = sorted_numbers.select { |i| i > number }
if bigger_than_me.last
pos = sorted_numbers.index(bigger_than_me.last) + 1
else
pos = 0
end
sorted_numbers.insert(pos, number)
end
end
sorted_numbers.reverse
end
Все это можно заменить на numbers.flatten.sort
Ориентиры:
a = [[1, 4, 7], [2, 4, 8], [3, 6, 9]]
n = 50000
Benchmark.bm do |b|
b.report { n.times { m(a) } }
b.report { n.times { a.flatten.sort } }
end
Выдает:
user system total real
2.940000 0.380000 3.320000 ( 7.573263)
0.380000 0.000000 0.380000 ( 0.892291)
Итак, мой алгоритм работает ужасно, yey!
Ответ 5
повторно
Python - 74 символа (подсчет пробелов и символов перевода строк)
def m(i):
y=[];x=sum(i,[])
while x:n=min(x);y+=[n];x.remove(n)
return y
i
вводится как список списков
Использование:
>>> m([[1,5],[6,3]])
[1, 3, 5, 6]
Ответ 6
Haskell: 127 символов (без отступов и символов перевода строки)
m l|all null l=[]
|True=x:(m$a++(xs:b))
where
n=filter(not.null)l
(_,min)=minimum$zip(map head n)[0..]
(a,((x:xs):b))=splitAt min n
В основном он обобщает слияние двух списков.
Ответ 7
Я просто оставлю это здесь...
Язык: C, Char count: 265
L[99][99];N;n[99];m[99];i;I;b=0;main(char t){while(scanf("%d%c",L[i]+I,&t)+1){++
I;if(t==10){n[i++]=I;I=0;}}if(I)n[i++] = I;N=i;while(b+1){b=-1;for(i=0;i<N;++i){
I=m[i];if(I-n[i])if(b<0||L[i][I]<L[b][m[b]])b=i;}if(b<0)break;printf("%d ",L[b][
m[b]]);++m[b];}puts("");}
Принимает такие значения:
1 4 7
2 5 8
3 6 9
(EOF)
Ответ 8
F #: 116 символов:
let p l=
let f a b=List.filter(a b) in
let rec s=function[]->[]|x::y->s(f(>)x y)@[x]@s(f(<=)x y) in
[for a in l->>a]|>s
Примечание: этот код заставляет F # выкидывать много предупреждений, но он работает:)
Здесь аннотированная версия с простыми и значимыми идентификаторами (обратите внимание: код выше не использует синтаксиС#light, код ниже):
let golf l=
// filters my list with a specified filter operator
// uses built-in F# function
// ('a -> 'b -> bool) -> 'a -> ('b list -> 'b list)
let filter a b = List.filter(a b)
// quicksort algorithm
// ('a list -> 'a list)
let rec qsort =function
| []->[]
| x :: y -> qsort ( filter (>) x y) @ [x] @ qsort ( filter (<=) x y)
// flattens list
[for a in l ->> a ] |> qsort
Ответ 9
Хотя у меня не было терпения, чтобы попробовать это, мой коллега показал мне способ, которым это возможно сделать, используя 0 символьный ключ - Whie Space
Ответ 10
(все остальные решения - O (N) (для введенного ввода))
Если N - количество элементов в выходе и k - количество входных списков, то вы не можете делать быстрее, чем O (N log k) - предположим, что каждый список был только одним элементом и у вас будет более быстрая сортировка по сравнению с O (N log N).
Те, кого я смотрел, больше похожи на O (N * k).
Вы можете довольно легко перейти к O (N log k) времени: просто поместите списки в кучу. Это один из способов эффективной сортировки ввода-вывода (вы можете также обобщать quicksort и heaps/heapsort).
[нет кода, только комментарий]
Ответ 11
С#
static void f(params int[][] b)
{
var l = new List<int>();
foreach(var a in b)l.AddRange(a);
l.OrderBy(i=>i).ToList().ForEach(Console.WriteLine);
}
static void Main()
{
f(new int[] { 1, 4, 7 },
new int[] { 2, 5, 8 },
new int[] { 3, 6, 9 });
}
Ответ 12
Javascript
function merge(a) {
var r=[], p;
while(a.length>0) {
for (var i=0,j=0; i<a.length && p!=a[j][0]; i++)
if (a[i][0]<a[j][0])
j = i;
r.push(p = a[j].shift());
if (!a[j].length)
a.splice(j, 1);
}
return r;
}
Тест:
var arr = [[1, 4, 7], [2, 5, 8], [3, 6, 9]];
alert(merge(arr));
Ответ 13
VB обычно не является языком выбора для кодового гольфа, но здесь все равно.
Настройка -
Dim m1 As List(Of Integer) = New List(Of Integer)
Dim m2 As List(Of Integer) = New List(Of Integer)
Dim m3 As List(Of Integer) = New List(Of Integer)
Dim m4 As List(Of Integer) = New List(Of Integer)
m1.Add(1)
m1.Add(2)
m1.Add(3)
m2.Add(4)
m2.Add(5)
m2.Add(6)
m3.Add(7)
m3.Add(8)
m3.Add(9)
Dim m5 As List(Of List(Of Integer)) = New List(Of List(Of Integer))
m5.Add(m1)
m5.Add(m2)
m5.Add(m3)
Попытка в VB.NET(без сортировки)
While m5.Count > 0
Dim idx As Integer = 0
Dim min As Integer = Integer.MaxValue
For k As Integer = 0 To m5.Count - 1
If m5(k)(0) < min Then min = m5(k)(0) : idx = k
Next
m4.Add(min) : m5(idx).RemoveAt(0)
If m5(idx).Count = 0 Then m5.RemoveAt(idx)
End While
Другая попытка VB.NET(с разрешенной сортировкой)
Private Function Comp(ByVal l1 As List(Of Integer), ByVal l2 As List(Of Integer)) As Integer
Return l1(0).CompareTo(l2(0))
End Function
.
.
.
While m5.Count > 0
m5.Sort(AddressOf Comp)
m4.Add(m5(0)(0)) : m5(0).RemoveAt(0)
If m5(0).Count = 0 Then m5.RemoveAt(0)
End While
Вся программа -
Dim rand As New Random
Dim m1 As List(Of Integer) = New List(Of Integer)
Dim m2 As List(Of Integer) = New List(Of Integer)
Dim m3 As List(Of Integer) = New List(Of Integer)
Dim m4 As List(Of Integer) = New List(Of Integer)
Dim m5 As List(Of List(Of Integer)) = New List(Of List(Of Integer))
m5.Add(m1)
m5.Add(m2)
m5.Add(m3)
For Each d As List(Of Integer) In m5
For i As Integer = 0 To 100000
d.Add(rand.Next())
Next
d.Sort()
Next
Dim sw As New Stopwatch
sw.Start()
While m5.Count > 0
Dim idx As Integer = 0
Dim min As Integer = Integer.MaxValue
For k As Integer = 0 To m5.Count - 1
If m5(k)(0) < min Then min = m5(k)(0) : idx = k
Next
m4.Add(min) : m5(idx).RemoveAt(0)
If m5(idx).Count = 0 Then m5.RemoveAt(idx)
End While
sw.Stop()
'Dim sw As New Stopwatch
'sw.Start()
'While m5.Count > 0
' m5.Sort(AddressOf Comp)
' m4.Add(m5(0)(0)) : m5(0).RemoveAt(0)
' If m5(0).Count = 0 Then m5.RemoveAt(0)
'End While
'sw.Stop()
Console.WriteLine(sw.Elapsed)
Console.ReadLine()
Ответ 14
Ruby:
41 значительный символ, 3 значительных пробельных символа в теле метода слияния.
arrs - массив массивов
def merge_sort(arrs)
o = Array.new
arrs.each do |a|
o = o | a
end
o.sort!
end
Чтобы проверить в irb:
arrs = [ [ 90, 4, -2 ], [ 5, 6, -100 ], [ 5, 7, 2 ] ]
merge_sort(arrs)
Возвращает: [-100, -2, 2, 4, 5, 6, 7, 90]
Изменить: Используется язык, сложенный слиянием/сортировкой, потому что он, вероятно, поддерживается кодом C и отвечает требованиям "быстрее". Я подумаю о решении позже (это выходные здесь, и я в отпуске).
Ответ 15
Perl: 22 символа, включая два значительных пробельных символа.
sub a{sort map{@$_}@_}
Здесь только встроены. Видеть?;)
Вызываем так:
my @sorted = a([1, 2, 3], [5, 6, 89], [13, -1, 3]);
print "@sorted" # prints -1, 1, 1, 2, 3, 3, 5, 6, 89
Честно говоря, отрицание языковых функций (примечание: не библиотеки...) кажется добрым - встретить точку. Самый короткий код для реализации на языке должен включать в себя функции buildins/language. Конечно, если вы импортируете модуль, вы должны считать этот код своим решением.
Изменить: удалить ненужный {} вокруг $_.
Ответ 16
F #, 32 символа
let f x=List.sort(List.concat x)
И без использования встроенной функции для concat (57 символов):
let f x=List.sort(Seq.toList(seq{for l in x do yield!l}))
Ответ 17
Я не думаю, что вы можете получить гораздо лучше, чем @Sykora, здесь, для Python.
Изменено для обработки ваших входов:
import heapq
def m(i):
return list(heapq.merge(*i))
print m(((1, 4, 7), (2, 5, 8), (3, 6, 9)))
Для фактической функции 59 символов или 52 в уменьшенной версии:
import heapq
def m(i): return list(heapq.merge(*i))
Это также имеет преимущество использования проверенной и истинной реализации, встроенной в Python
Изменить: Удалены полуколоны (спасибо @Douglas).
Ответ 18
Даже если это может нарушить правила. Здесь хорошая и короткая запись С++:
13 символов
l1.merge(l2); // Removes the elements from the argument list, inserts
// them into the target list, and orders the new, combined
// set of elements in ascending order or in some other
// specified order.
Ответ 19
Системные скрипты GNU (я думаю, что обман, но это хорошо знать).
sort -m file1 file2 file3 ...
Ответ 20
Реализация в C по связанным спискам.
http://datastructuresandalgorithmsolutions.blogspot.com/2010/02/chapter-2-basic-abstract-data-types_7147.html
Ответ 21
BASH примерно в 250 основных символах
BASH на самом деле не очень хорош в манипулировании списками, так или иначе это выполняется.
# This merges two lists together
m(){
[[ -z $1 ]] && echo $2 && return;
[[ -z $2 ]] && echo $1 && return;
A=($1); B=($2);
if (( ${A[0]} > ${B[0]} ));then
echo -n ${B[0]}\ ;
unset B[0];
else
echo -n ${A[0]}\ ;
unset A[0];
fi;
m "${A[*]}" "${B[*]}";
}
# This merges multiple lists
M(){
A=$1;
shift;
for x in [email protected]; do
A=`m "$A" "$x"`
done
echo $A
}
$ M '1 4 7' '2 5 8' '3 6 9'
1 2 3 4 5 6 7 8 9
Ответ 22
VB.NET(2008) 185 символов
принимает список (из списка (байта))
Function s(i)
s=New List(Of Byte)
Dim m,c
Dim N=Nothing
Do
m=N
For Each l In i:
If l.Count AndAlso(l(0)<m Or m=N)Then m=l(0):c=l
Next
If m<>N Then s.Add(m):c.Remove(m)
Loop Until m=N
End Function
Ответ 23
Python, 107 символов:
def f(l):
n=[]
for t in l:
for i in t: n+=[t]
s=[]
while n: s.+=[min(n)]; n.remove(min(n))
return s
Ответ 24
Haskell like (158, но более 24 пробелов можно удалить.):
mm = foldl1 m where
m [] b = b
m a [] = a
m (a:as) (b:bs)
| a <= b = a : m as (b:bs)
| true = b : m (a:as) bs
Ответ 25
Python, 181 символ
from heapq import *
def m(l):
r=[]
h=[]
for x in l:
if x:
heappush(h, (x[0], x[1:]))
while h:
e,f=heappop(h)
r.append(e)
if f:
heappush(h, (f.pop(0),f))
return r
Это выполняется в O (NlgM) времени, где N - общее количество элементов, а M - количество списков.
Ответ 26
В. Б.
Настройка:
Sub Main()
f(New Int32() {1, 4, 7}, _
New Int32() {2, 5, 8}, _
New Int32() {3, 6, 9})
End Sub
Выход:
Sub f(ByVal ParamArray b As Int32()())
Dim l = New List(Of Int32)
For Each a In b
l.AddRange(a)
Next
For Each a In l.OrderBy(Function(i) i)
Console.WriteLine(a)
Next
End Sub