Осуществить сортировку сна


74

Sleep Sort - алгоритм целочисленной сортировки, который я нашел в Интернете. Он открывает выходной поток, и для каждого входного числа параллельно задерживается число секунд и выводится это число. Из-за задержек наибольшее число будет выведено последним. Я оцениваю это имеет O (n + m), где n - количество элементов, а m - наибольшее число.

Вот оригинальный код на Bash

#!/bin/bash
function f() {
    sleep "$1"
    echo "$1"
}
while [ -n "$1" ]
do
    f "$1" &
    shift
done
wait

Вот псевдокод

sleepsort(xs)
 output = []
 fork
   for parallel x in xs:
     sleep for x seconds
     append x to output
 wait until length(output) == length(xs)
 return output

Ваша задача - реализовать функцию Sleep Sort на выбранном вами языке программирования. Вы можете пренебречь любыми факторами параллелизма, такими как состояние гонки, и никогда не блокировать какие-либо общие ресурсы. Самый короткий код выигрывает. Определение функции учитывает длину кода.

Список ввода ограничен только неотрицательными целыми числами, и ожидается, что длина списка ввода будет достаточно большой (протестируйте не менее 10 чисел), чтобы условия гонки никогда не возникали. и предполагая, что условия гонки никогда не случаются.


3
Что имеет значение для длины? Полные программы, включая IO или просто соответствующую программу?
Конрад Рудольф

8
Проблема с этим. В зависимости от порядка списка, вы можете не прочитать весь список до того, как будет напечатано первое значение. Например, большой список, для чтения которого требуется 45 секунд, первое значение равно 2, а последнее значение равно 1. Поток для печати 1 может быть выполнен после печати 2. К сожалению, выходные данные больше не сортируются должным образом. Могут быть некоторые обходные пути - создание потоков и их запуск после прочтения всего списка (но это приведет к увеличению длины кода по сравнению с гольфом). Интересно, может кто-нибудь предоставить гольф, который решит эту потенциальную проблему ... Я собираюсь попробовать.
Томас Оуэнс

11
Кстати, что делает этот алгоритм действительно интересным, так это то, что на самом деле существуют реальные приложения. Например, секвенирование ДНК (секвенирование Сэнгера) зависит от чего-то подобного для сортировки фрагментов ДНК в соответствии с их длиной (и, в более общем случае, каждый электрофорез делает что-то подобное). Разница в том, что последовательность выполняется физически, а не на компьютере.
Конрад Рудольф

12
Я ненавижу быть тем, кто идет на параде всех, но разве это не просто переложит сложность на планировщик ОС способом, который, вероятно, O (N ^ 2)?
Random832

1
Я думаю, что есть алгоритмы физической сортировки, которые занимают O (n) времени, но O (n) физических объектов. Ну, мы можем использовать тающие свечи и трубку, чтобы сделать это. ru.wikipedia.org/wiki/Spaghetti_sort
Мин-Тан,

Ответы:


17

Этакая неудачная попытка Perl , 59 55 52 38 32 персонажа :

map{fork||exit print sleep$_}@a

Barebones: 25 символов:

... если вы не возражаете против результатов сортировки:

map{fork||die sleep$_}@a

Со всеми отделками:

(для максимального соответствия требованиям, 44 символа)

sub t{map{fork||exit print sleep$_}@_;wait}

Если вы позволите Perl ждать вас, 39 символов:

sub t{map{fork||exit print sleep$_}@_}

И снова, если не возражаете die(), 32 символа ...

sub t{map{fork||die sleep$_}@_}

Обратите внимание, что в Perl 6, или когда объявлена ​​функция 'say', можно заменить printфункцию say, сохраняя символ в каждом экземпляре. Очевидно, что, поскольку dieоба завершают разветвленный процесс и записывают вывод, это остается самым коротким решением.


Вы можете запустить, perl-Eчтобы включить 5.010 функций, таких какsay
mbx

(fork&&die sleep$_)for@aтоже работает
malkaroee

20

C , 127 символов, довольно очевидное решение:

main(int c,char**v){
#pragma omp parallel for num_threads(c)
for(int i=1;i<c;++i){int x=atoi(v[i]);sleep(x);printf("%d ",x);}}

(Составлено gcc -std=c99 -fopenmp sort.cи игнорируется все предупреждения.)


4
Круто, я действительно должен изучить Opemp
Нильс

Я бы назвал это 93 символами (без разбора командной строки и тому подобного), но впечатляет, что вы можете сделать это всего за 34 дополнительных символа в C!
Рекс Керр

1
@KonradRudolph - Вы можете сохранить 6 байт , идущие в обратном направлении: for(;c>=0;--c){int x=atoi(v[c]);. Не уверен, что это разрешено.
owacoder

15

Ruby 1.9, 32 символа

Как функция:

s=->a{a.map{|i|fork{p sleep i}}}

Если мы можем просто использовать предопределенную переменную, она сокращается до 25 символов:

a.map{|i|fork{p sleep i}}

1
Вы можете сохранить несколько символов с помощью Thread.new{p sleep i}распечатки вывода.
Говард

@ Ховард: Хороший улов, спасибо!
Вентеро

@ Ventero, я только изучаю Ruby, и я хотел бы знать, как вы будете запускать эту лямбда-функцию или, более конкретно, как вы вводите это. Можно ли запустить с IRB? Спасибо!
Бен Хили

14

JavaScript , 65 символов (в зависимости от того, используете ли вы console.logили что-то еще для вывода результата)

a.map(function(v){setTimeout(function(){console.log(v)},v*1000)})

Предполагается, что aэто массив неотрицательных целых чисел, который map()существует в прототипе массива (JavaScript 1.6+).


1
Вероятно, вы можете сбрить два или даже три символа, умножив на 10 (или 9) вместо 1000, не ставя под угрозу правильность.
Конрад Рудольф

12
Если одна секунда предназначена, чтобы остаться, вы можете использовать 1e3вместо этого.
Джои

2
@Tomalak alert- блокирующий вывод promptJavaScript, блокирующий ввод confirmJavaScript и блокирующий двоичный ввод JavaScript. Если бы JS записывался в командной строке, это были бы вызовы, которые вы бы использовали.
zzzzBov

1
@zzzzBov, использование блокирования вывода почти наверняка будет плохой идеей для этой задачи.
Питер Тейлор

2
@zzzzBov, я беспокоюсь о том, в каком порядке они называются, если только у спецификации JS нет строгих гарантий порядка, в котором вызываются thunks, поставленные в setTimeout.
Питер Тейлор

13

APL ( 15 13)

{⎕←⍵⊣⎕DL⍵}&¨⎕

Что оно делает:

¨⎕       : for each element of the input
&        : do on a separate thread
⎕DL⍵    : wait approx. ⍵ seconds
⎕←⍵     : output ⍵

Я вижу коробки вместо 3 символов.
defhlt

8
@ArtemIce: там должно быть три коробки (четырехугольника). Два - переменная ввода / вывода (чтение, которое получает вход, и запись в него, печатает вывод), и одно находится в названии ⎕DLфункции, которая является sleep.
marinus

9

Четыре попытки в Эрланге:

Вывод на консоль позволил себе сделать это каждый, 9ms * Numberпоскольку этого достаточно, чтобы заставить его работать (протестировано на встроенной плате Atom = медленно):

Нужно 60 символов

s(L)->[spawn(fun()->timer:sleep(9*X),io:write(X)end)||X<-L].

Вывод на консоль полный не-Erlangish, поэтому Pвместо этого мы отправляем сообщение для обработки :

Необходимо 55 символов

s(P,L)->[spawn(fun()->timer:sleep(9*X),P!X end)||X<-L].

Отправка через некоторое время также может быть выполнена по-другому (это работает даже с 1ms * Number):

Требуется 41 символ

s(P,L)->[erlang:send_after(X,P,X)||X<-L].

На самом деле это немного несправедливо, поскольку встроенная функция send_afterзапаздывает и требует erlang:префикса пространства имен , если мы считаем это пространство имен импортированным (сделано на уровне модуля):

Необходимо 34 символа

s(P,L)->[send_after(X,P,X)||X<-L].

7

C # - 137 символов

Вот ответ в C # (обновленный со степенями параллелизма как прокомментировано)

void ss(int[]xs){xs.AsParallel().WithDegreeOfParallelism(xs.Length).Select(x=>{Thread.Sleep(x);return x;}).ForAll(Console.WriteLine);}

1
Вам нужно будет указать, WithDegreeOfParallelismчтобы это работало, аналогично тому, как num_threadsв моем коде OpenMP C.
Конрад Рудольф

120 байтов:void m(int[] l){foreach(var i in l){var t=new Thread(()=>{Thread.Sleep(int.Parse(s));Console.Write(s);});t.Start();}}}
MrPaulch

@MrPaulch Обратите внимание, что вам нужно снова присоединиться к потокам, если вы хотите, чтобы ваша программа
еще один Geek

Почему? Самая длинная нить будет поддерживать процесс.
MrPaulch

7

Питон - 81 93 148 150 153

Настройка кода @ BiggAl, так как это игра, в которую мы играем ....

import threading as t,sys
for a in sys.argv[1:]:t.Timer(int(a),print,[a]).start()

... или 97 175 с отложенным началом резьбы

import threading as t,sys
for x in [t.Timer(int(a),print,[a]) for a in sys.argv[1:]]:x.start()

Принимает ввод через командную строку, аля

./sleep-sort\ v0.py 1 7 5 2 21 15 4 3 8

Как и во многих питон-гольфах, наступает момент, когда код достаточно компактен, чтобы псевдонимы переменных для сокращения имен даже не сохраняли символы.

Хотя это и забавно, потому что он псевдоним sys и создает потоки ОБА как t, поэтому sys.argv становится t.argv. Короче, чем от импорта foo *, и чистая экономия символов! Однако я полагаю, что Гвидо не будет доволен ...

Примечание для самостоятельного изучения c и прекращения игры в гольф на питоне. СВЯТАЯ КОРОВА, ЭТО МЕНЬШЕ, ЧЕМ РЕШЕНИЕ C!


Мне удалось внести некоторые изменения, но форматирование не очень хорошо отображается в комментариях, поэтому я сделал свой собственный ответ. daemonне требует настройки, если вы не запускаете это как демон, и короче использовать позиционные аргументы, esp. если вы псевдоним NoneнаN
theheadofabroom

Да, и первый не работает для меня под 2.7.1, как jкажется, Falseпобочным эффектом попытки сделать слишком много в одной строке?
глава

дерьмо, я не осознавал, что вы можете импортировать несколько модулей в один и тот же псевдоним - я мог бы подумать о некоторых случаях использования для этого, когда у меня есть несколько подклассов одного и того же пользовательского базового класса, лежащих в отдельных подмодулях. Если мы сможем сбрить еще 30, это короче, чем bash ... Но я не думаю, что это произойдет.
головной убор

Да, причина, по которой я не знал, в том, что вы не можете - я просто попытался запустить его, а многопоточность - это не псевдоним, а просто поточная. Это системный псевдоним T ... Вы пытались запустить это? Это только дополнительные 2 символа на каждого, хотя их нужно импортировать, as t,sа затем изменить sнаsys
theheadofabroom

1
почему вы не используете printфункцию вместо sys.stdout.write?
летающая овца

6

Для забавы, вот версия ColdFusion (8+) ;-) В ней 109 символов, не считая переносы строк и отступы, которые я добавил для удобочитаемости здесь.

<cfloop array="#a#" index="v">
  <cfthread><cfthread action="sleep" duration="#v*1000#"/>#v#</cfthread>
</cfloop>

Это предполагает, что <cfoutput>это в силе. Несколько символов можно сохранить, написав все в одной строке.


6

Ява (иначе никогда не выигрывает в Codegolf): 234 211 187 символов

public class M{public static void main(String[]s){for(final String a:s)new Thread(){public void run(){try{sleep(Long.parseLong(a));}catch(Exception e){}System.out.println(a);}}.start();}}

ungolfed:

public class M {
    public static void main(String[] s) {
        for(final String a:s) new Thread(){
            public void run() {
                try {
                    sleep(Long.parseLong(a));
                } catch(Exception e){}
                System.out.println(a);
            }
        }.start();
    }
}

@ Джои, спасибо за то, что поправил.
Истина

Класс может быть закрытым, сохраняя 7 символов.
Даниэль Любаров

1
Вы также можете объявить throws Throwableи избавиться от catchоговорки.
Даниэль Любаров

Я думаю, что вы можете сохранить 2 байта, заменив Long.parseLongна Long.valueOf.
HyperNeutrino

Я знаю, что прошло 6,5 лет, но вы можете играть в гольф некоторыми частями: publicи finalмогут быть удалены; class M{public static void mainможет быть interface M{static void main(Java 8+); Long.parseLong(a)может быть new Long(a)(в результате получается 165 байтов )
Кевин Круйссен

5

Javascript - 52 символа

for(i in a)setTimeout("console.log("+a[i]+")",a[i])

Добро пожаловать в CodeGolf.SE! Я отформатировал ваш ответ для вас, в частности выделив ваш код четырьмя пробелами, чтобы он отображался как код. Вы найдете другую справку по форматированию на боковой панели страницы редактирования.
dmckee

5

Скала - 42 40 символов (особый случай)

Если у вас есть пул потоков, по крайней мере, размер числа элементов списка:

a.par.map{i=>Thread.sleep(i);println(i)}

Скала - 72 символа (общее)

a.map(i=>new Thread{override def run{Thread.sleep(i);println(i)}}.start)

afaik вы не используете, {}оставаясь на одной линии.
летающая овца

@ Летающая овца - Вы можете опустить {}одно утверждение , но оно все равно необходимо для группировки вещей, разделенных ;одной строкой или нет. И {}в некоторых случаях вы можете писать многострочные операторы (например, если / еще).
Рекс Керр

о, я не имел в виду, что вы можете опустить их, но ()вместо этого вы можете использовать их для однострочников. это вопрос вкуса, я думаю. (я просто не понимаю, почему ()вообще поддерживается, когда {}их заменяют. возможно, чтобы не отталкивать пользователей java мгновенно). Scala - это круто, но определение блоков кода с помощью отступов явно лучше. (и так начинается религиозная война;))
летающие овцы

@ Летающие овцы - Вы дезинформированы. Вы можете использовать ()для отдельных заявлений. Попробуйте войти (1 to 9).map(i => {val j = i+1; i*j})в REPL и посмотрите, что произойдет, если вы используете (1 to 9).map(i => (val j = i+1; i*j)).
Рекс Керр

правда, но я ссылался только на выражения и прочее. Извините, я ненавижу писать вещи, не имея возможности использовать переносы строк;)
летающая овца

4

Хаскель - 143 персонажа

import Control.Concurrent
import System
d=threadDelay
f x=d(10^6*x)>>print x
g s=mapM(forkIO.f)s>>d(10^6*maximum s+1)
main=getArgs>>=g.map read

Вероятно, это можно было бы сделать короче, приняв ввод в stdin, если бы это была опция, но уже поздно, и в любом случае, для самой функции это все еще 104 символа.


4

Befunge-98, 38 31 байт

Я знаю, что это старая проблема, но я недавно обнаружил как спящие, так и двумерные языки, подумал о том, как их объединить, и искал место для его публикации, так что мы здесь.

&#vt6j@p12<'
v:^ >$.@
>:!#^_1-

Основной IP-адрес читает число ( &), а затем обращается к тому, tкто его клонирует: один переходит на ту же строку и циклически повторяет, считывает новые числа и генерирует новые дочерние элементы, пока не достигнет EOF, который завершает последовательность. Все дочерние процессы застревают в замкнутом цикле ( vи ^в третьем столбце) до тех пор, пока основной IP не завершит чтение ввода и не выполнит последовательность команд '<21p, которая переводит символ <в положение (1,2), перезаписывает ^и освобождает все дочерние процессы, которые начинают одновременно цикл, уменьшая на 1 их число на каждой итерации. Так как скорость выполнения различных IP-адресов синхронизируется в befunge, они будут завершаться (и печатать свои значения) по порядку, сортируя входной список.


26 байтов , немного перемещая поток управления.
Джо Кинг,

3

Немного опоздал на вечеринку:

Клен - 91 83 символа

В 91:

M:=():use Threads in p:=proc(n)Sleep(n);:-M:=M,n;end:Wait(map(i->Create(p(i)),L)[])end:[M];

В 83:

M:=():use Threads in Wait(seq(Create(proc(n)Sleep(n);:-M:=M,n end(i)),i=L))end:[M];

(Для этого требуется версия Maple 15 и ожидается, что список будет отсортирован в L)


3

C 70 69 символов

Не ждет возврата дочерних процессов, иначе работает.

main(n) {
    while(~scanf("%d",&n)?fork()||!printf("%d\n",n,sleep(n)):0);
}

2

PHP 57 байт

<?for(;$i=fgets(STDIN);)pcntl_fork()?:die($i.usleep($i));

pcntl_fork () доступен только в Linux.


2

Баш (38):

xargs -P0 -n1 sh -c 'sleep $0;echo $0'

Изменить: Плавающая точка от стандартного ввода, разделенных пробелами или переводами строки.


2

Хаскелл, 90

import Control.Concurrent
s::[Int]->IO()
s=mapM_(\x->forkIO$threadDelay(x*9999)>>print x)

Я надеюсь, что это соответствует всем требованиям.



1

Просто некоторые изменения из версии @rmckenzie:

Python отложил начало потока в 155 152 114 108 107:

import sys, threading as t
for x in [t.Timer(int(a),sys.stdout.write,[a]) for a in sys.argv[1:]]:x.start()

Python без задержки в 130 128 96 95 93:

import sys,threading as t
for a in sys.argv[1:]:t.Timer(int(a),sys.stdout.write,[a]).start()

Управлял еще несколькими оптимизациями, используя Timerвместо Threadнего более лаконичный вызов и устранял необходимость в импорте time. Метод запуска с отложенным потоком выигрывает от понимания списка, так как устраняет необходимость отдельно инициализировать список в начале, хотя он на два символа длиннее ( "["+"]"+" "-":"), чем цикл for, поэтому он бесполезен без отложенного запуска, и вы должны быть осторожны при использовании списка а не генератор, или вы фактически не создаете потоки таймера, пока не разберетесь с генератором.

У кого-нибудь еще есть оптимизация?


Трюк с asподсказками, но в 2.7.1 вы можете импортировать только один модуль в псевдоним, и после некоторой игры с ним вы даже не сможете import mod1,mod2 as a,b, вы должны это сделать import mod1 as a, mod2 as b. Он по-прежнему сохраняет несколько символов, но это не совсем лекарство - все, о чем я думал ... И на самом деле лучше оставить sys в качестве sys. Псевдоним потоков все еще помогает, хотя ...


Вы меня били, есть апдинг. Мне нравится х = []; х + = []. Не знал, что вы могли бы сделать это ....
arrdem

... вы могли бы сделать это в 128, если бы вы потеряли пробелы между: [оператор] в вашем цикле и f (x) ... каким-то образом я получил его до 127, но я думаю, что это не считая заключительный перевод строки (который законно в CG). Думаю, я бы дал вам обновление, а не инструмент и кражу вашего кода.
arrdem

@rmckenzie пойти на это, я украл твой. Мне всегда интересно видеть CG'd Python - я чувствую, что делаю что-то очень извращенное, учитывая цели языка ...
theheadofabroom

Да, я, честно говоря, шокирован тем, как разборчиво большинство питоновских гольфов остаются ... ценой "стеклянного пола" персонажей. Проверьте это: импортируйте потоки, sys as t
arrdem

1

Clojure, 54

(defn s[n](doseq[i n](future(Thread/sleep i)(prn i))))


Вы можете избавиться от нескольких символов, вставив пропуск defn(его скобки + список аргументов: от 54 до 43 chrs), или использовать fnвместо defn=> len- = 2, так что я бы сказал в clj его 43: D
test30

1

Ржавчина - 150 байт

И именно поэтому вы не пишете гольф в Rust, он более многословен, чем Java;). Зависит от внешнего ящика crossbeam, было бы еще хуже без него.

|x|{extern crate crossbeam;crossbeam::scope(|s|for&v in x{s.spawn(move||{std::thread::sleep(std::time::Duration::from_secs(v));println!("{}",v)});})}

Полная тестовая программа:

fn main() {
    let z =
    |x|{extern crate crossbeam;crossbeam::scope(|s|for&v in x{s.spawn(move||{std::thread::sleep(std::time::Duration::from_secs(v));println!("{}",v)});})}
    ;
    z(&[4, 2, 3, 5, 7, 8, 9, 1, 6, 10])
}

0

Скучно, порт C #, просто чтобы снова начать работать с языком:

F # - 90 символов

PSeq.withDegreeOfParallelism a.Length a|>PSeq.iter(fun x->Thread.Sleep(x);printfn "%A" x)

0

JavaScript, 74

function(a){for(i=0;i<a.length;i++)setTimeout('alert('+a[i]+')',a[i]*1e3)}

или 71/65 символов с нестандартностью:

function(a){a.map(function(v){setTimeout('console.log('+v+')',v*1e3)})}

Я думаю, что даже в 2011 году, function(a){a.map(function(v){setTimeout(console.log,v,v)})}по крайней мере, в одном браузере работало по 60 байт. Конечно, в эти дни вы бы написали a=>a.map(v=>setTimeout(console.log,v,v))вместо.
Нил


0

VB.NET 100 байт

Поскольку VB.Net требует, чтобы однострочные лямбды содержали только одну инструкцию, этот код должен иметь несколько строк:

Array.ForEach(i, Async Sub(x)
Await Threading.Tasks.Task.Delay(x*1000)
Console.WriteLine(x)
End Sub)

Ungolfed:

Option Strict Off

Sub Main(i() as String)
    Array.ForEach(i, Async Sub(x)
                         Await Threading.Tasks.Task.Delay(x * 1000)
                         Console.WriteLine(x)
                     End Sub)
End Sub

Однако я не уверен, что вы подсчитываете операторы импорта в счетчиках байтов, потому что, если вы их не учитываете, я мог бы написать так:

VB.NET 71 байт

a.ForEach(i, Async Sub(x)
Await t.Delay(x*1000)
c.WriteLine(x)
End Sub)

Ungolfed:

Option Strict Off
Imports t = System.Threading.Tasks.Task
Imports c = System.Console
Imports a = System.Array

Sub Main(i() as String)
    a.ForEach(i, Async Sub(x)
                     Await t.Delay(x * 1000)
                     c.WriteLine(x)
                 End Sub)
End Sub

0

Groovy, 47 байтов

Предполагается, что номера приведены в командной строке ...

args.each{i->Thread.start{sleep(i*22)print i}}


0

Mathematica, 34 или 36 байт

RunScheduledTask[Print@#,{#,1}]&/@

Просто добавьте список к концу этого кода и оцените. Если оно должно быть допустимым определением функции, тогда требуется два дополнительных байта:

RunScheduledTask[Print@#,{#,1}]&/@#&

0

C ++ 11, 229 байт

#import<future>
#import<iostream>
using namespace std;int main(int a,char**v){auto G=new future<void>[a];while(--a){G[a]=move(async([=](){this_thread::sleep_for(chrono::seconds(atoi(v[a])));cout<<v[a]<<" "<<flush;}));}delete[]G;}

Ungolfed и использование:

#import<future>
#import<iostream>
using namespace std;
int main(int a,char**v){
 auto G=new future<void>[a];
 while(--a){
  G[a]=move(async(
   [=](){
    this_thread::sleep_for(chrono::seconds(atoi(v[a])));
    cout<<v[a]<<" "<<flush;
   }
  ));
 }
 delete[]G;
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.