Если у Вас есть корневой доступ к серверу, простой способ решить такие проблемы состоит в том, чтобы выполнить sshd в режиме отладки путем издания чего-то как /usr/sbin/sshd -d -p 2222
на сервере (полный путь к sshd требуемому исполняемому файлу, which sshd
может помочь), и затем соединяющийся от клиента с ssh -p 2222 user@host
. Это вынудит демона SSH остаться на переднем плане и отобразить отладочную информацию о каждом соединении. Ищите что-то как
debug1: trying public key file /path/to/home/.ssh/authorized_keys
...
Authentication refused: bad ownership or modes for directory /path/to/home/
Если не возможно использовать альтернативный порт, можно временно остановить демона SSH и заменить его одним в режиме отладки. Остановка демона SSH не уничтожает существующие соединения, таким образом, возможно сделать это через удаленный терминал, но несколько опасный - если соединение действительно становится поврежденным так или иначе в то время, когда замена отладки не работает, Вы заблокированы из машины, пока Вы не можете перезапустить его. Команды потребовали:
service ssh stop
/usr/sbin/sshd -d
#...debug output...
service ssh start
(В зависимости от Вашего дистрибутива Linux сначала / последняя строка могла бы быть systemctl stop sshd.service
/ systemctl start sshd.service
вместо этого.)
Если бы я должен был применить ту перестановку к деке игральной карты, я думаю, что сначала переставил бы деку, то отобразил бы карты подряд на моих глазах и обрабатывающий слева направо, везде, где существуют смежные клубы, или основа... перемещают всех кроме одного из тех наугад где-то в другом месте (хотя не рядом с другим того же типа).
Например, рукой как
После основной перестановки:
< > < >
1 2 3
две группы смежных лопат, мы должны переместить 1, 2 и 3. Для 1, выбор:
↑ ↑ ↑ ↑
Мы выбираем тот наугад от тех 4. Затем мы повторяем процесс для 2 и 3.
Реализованный в perl
это было бы:
shuf list | perl -e '
@songs = map {/(.*?)-/; [$1,$_]} <>;
for ($i = 0; $i < @songs; $i++) {
if (($author = $songs[$i]->[0]) eq $previous) {
my @reloc_candidates, $same;
for($j = 0; $j < @songs; $j++) {
# build a list of positions where we could move that song to
if ($songs[$j]->[0] eq $author) {$same = 1} else {
push @reloc_candidates, $j unless $same;
$same = 0;
}
}
push @reloc_candidates, $j unless $same;
if (@reloc_candidates) {
# now pick one of them at random:
my $chosen = $reloc_candidates[int(rand(@reloc_candidates))];
splice @songs, $chosen - ($chosen > $i), 0, splice @songs, $i, 1;
$i -= $chosen > $i;
}
}
$previous = $author;
}
print map {$_->[1]} @songs'
Это найдет решение с несмежными художниками, если это будет существовать (если больше чем половина песен не от того же художника), и должен быть универсальный AFAICT.
Ваши данные в качестве примера и ограничения на самом деле только позволяют несколько решений — необходимо играть John B. любая песня, например. Я собираюсь предположить, что Ваш фактический полный плей-лист не по существу John B со случайным другим материалом для разбивания его.
Это - другой случайный подход. В отличие от решения @frostschutz, это работает быстро. Это не гарантирует результат, который соответствует Вашим критериям, как бы то ни было. Я также представляю второй подход, который работает над Вашими данными в качестве примера — но я подозреваю, приведет к плохим результатам на Ваших реальных данных. Запутывая Ваши реальные данные, я добавляю подход 3 — который является случайной универсальной формой, кроме него избегает двух песен тем же художником подряд. Обратите внимание, что это только делает 5, "тянет" в "деку" остающихся песен, если после этого это все еще столкнется с дублирующимся художником, то это произведет ту песню так или иначе — этот путь, ее гарантируемый, который на самом деле закончит программа.
В основном это генерирует плей-лист в каждой точке, спрашивая, "от каких художников у меня все еще есть неиграемые песни?" Затем выбирая случайного художника и наконец случайную песню от того художника. (Таким образом, каждый художник взвешивается одинаково, не в пропорции к количеству песен.)
Дайте ему попытку на своем фактическом плей-листе и посмотрите, приводит ли это к лучшим результатам, чем однородно случайный.
Использование: ./script-file < input.m3u > output.m3u
Удостоверьтесь, что chmod +x
это, конечно. Обратите внимание, что это не обрабатывает строку для подписи, которая является наверху некоторых файлов M3U правильно..., но Ваш пример не имел этого.
#!/usr/bin/perl
use warnings qw(all);
use strict;
use List::Util qw(shuffle);
# split the input playlist by artist
my %by_artist;
while (defined(my $line = <>)) {
my $artist = ($line =~ /^(.+?) - /)
? $1
: 'UNKNOWN';
push @{$by_artist{$artist}}, $line;
}
# sort each artist's songs randomly
foreach my $l (values %by_artist) {
@$l = shuffle @$l;
}
# pick a random artist, spit out their "last" (remeber: in random order)
# song, remove from the list. If empty, remove artist. Repeat until no
# artists left.
while (%by_artist) {
my @a_avail = keys %by_artist;
my $a = $a_avail[int rand @a_avail];
my $songs = $by_artist{$a};
print pop @$songs;
@$songs or delete $by_artist{$a};
}
Как второй подход, вместо выбора случайный художник, которого можно использовать, выбирает художника с большинством песен, который является также не последним художником, которого мы выбрали. Заключительный абзац программы затем становится:
# pick the artist with the most songs who isn't the last artist, spit
# out their "last" (remeber: in random order) song, remove from the
# list. If empty, remove artist. Repeat until no artists left.
my $last_a;
while (%by_artist) {
my %counts = map { $_, scalar(@{$by_artist{$_}}) } keys %by_artist;
my @sorted = sort { $counts{$b} <=> $counts{$a} } shuffle keys %by_artist;
my $a = (1 == @sorted)
? $sorted[0]
: (defined $last_a && $last_a eq $sorted[0])
? $sorted[1]
: $sorted[0];
$last_a = $a;
my $songs = $by_artist{$a};
print pop @$songs;
@$songs or delete $by_artist{$a};
}
Остальная часть программы остается такой же. Обратите внимание, что это безусловно не самый эффективный способ сделать это, но это должно быть достаточно быстро для плей-листов любого нормального размера. С Вашими данными в качестве примера все сгенерированные плей-листы запустятся с песни John B., затем песня Anna A., затем песня John B. После этого это намного менее предсказуемо (как все, но John B. имеет одну песню в запасе). Обратите внимание, что это принимает Perl 5.7 или позже.
Использование совпадает с предыдущими 2. Отметьте 0..4
часть, это - то, куда 5 попыток макс. прибывают из. Вы могли количество попыток, например, 0..9
дал бы 10 общих количеств. (0..4
= 0, 1, 2, 3, 4
, то, которое Вы заметите, является на самом деле 5 объектами).
#!/usr/bin/perl
use warnings qw(all);
use strict;
# read in playlist
my @songs = <>;
# Pick one randomly. Check if its the same artist as the previous song.
# If it is, try another random one. Try again 4 times (5 total). If its
# still the same, accept it anyway.
my $last_artist;
while (@songs) {
my ($song_idx, $artist);
for (0..4) {
$song_idx = int rand @songs;
$songs[$song_idx] =~ /^(.+?) - /;
$artist = $1;
last unless defined $last_artist;
last unless defined $artist; # assume unknown are all different
last if $last_artist ne $artist;
}
$last_artist = $artist;
print splice(@songs, $song_idx, 1);
}
sed 's/ - .*//' output.m3u | uniq -d
). И Вы могли объяснить, заботится ли это о некоторых художниках, не заканчивающих в начале или конце плей-листа?
– Teresa e Junior
04.11.2013, 19:50
Если Вы не возражаете против него являющийся ужасно неэффективным...
while [ 1 ]
do
R="`shuf playlist`"
D="`echo "$R" | sed -e 's/ - .*//' | uniq -c -d`"
if [ "$D" == "" ]
then
break
#else # DEBUG ONLY:
# echo --- FAIL: ---
# echo "$D"
# echo -------------
fi
done
echo "$R"
Это просто продолжает прокручиваться и прокручиваться, пока это не наталкивается на результат, который не имеет двух или больше Johns подряд. Если существуют так многие Johns в Вашем плей-листе, что такая комбинация не существует, или крайне маловероятно быть прокрученным, ну, в общем, это зависнет.
Результат в качестве примера с Вашим входом:
John B. - Song 4
Kyle C. - Song 1
Anna A. - Song 2
John B. - Song 3
Anna A. - Song 1
John B. - Song 1
U--Rock - Song 1
John B. - Song 2
I--Rock - Song 1
John B. - Song 5
Если Вы не прокомментируете строки отладки, то это скажет Вам, почему это перестало работать:
--- FAIL: ---
3 John B.
-------------
--- FAIL: ---
2 John B.
2 John B.
-------------
Это должно помочь определить причину в случае, если она зависает неограниченно долго.
sort
разработан.
– Teresa e Junior
01.11.2013, 21:53
shuf
переставляет плей-лист в 80 раз быстрее, чем sort -R
. Я не знал это также! Я оставлю его работающий в течение 15 минут с shuf
, возможности выше!
– Teresa e Junior
01.11.2013, 21:57
echo "$D"
перед if
. Это должно сказать Вам, какие дубликаты предотвратили результат того, чтобы быть выбранным. Это должно сказать Вам, где искать проблему. (Редактирование: Добавленная возможная отладка кодирует к ответу.)
– frostschutz
01.11.2013, 22:07
sort
или shuf
.
– Teresa e Junior
01.11.2013, 22:24
Другой подход с помощью Bash. Это читает плей-лист в произвольном порядке, пытается вставить строку в другом конце списка, если это - дубликат и откладывает единственную простофилю для перевставки его в другое место. Это перестало работать, если существуют тройные дубликаты (сначала, в последний раз, и отложенный идентичный), и это добавит те плохие записи в самый конец списка. Это, кажется, может решить обширный список, который Вы загрузили большую часть времени.
#!/bin/bash
first_artist=''
last_artist=''
bad_artist=''
bad_line=''
result=''
bad_result=''
while read line
do
artist=${line/ - */}
line="$line"$'\n'
if [ "$artist" != "$first_artist" ]
then
result="$line""$result"
first_artist="$artist"
# special case: first = last
if [ "$last_artist" == '' ]
then
last_artist="$artist"
fi
# try reinserting bad
if [ "$bad_artist" != '' -a "$bad_artist" != "$first_artist" ]
then
first_artist="$bad_artist"
result="$bad_line""$result"
bad_artist=''
bad_line=''
fi
elif [ "$artist" != "$last_artist" ]
then
result="$result""$line"
last_artist="$artist"
# try reinserting bad
if [ "$bad_artist" != '' -a "$bad_artist" != "$last_artist" ]
then
last_artist="$bad_artist"
result="$result""$bad_line"
bad_artist=''
bad_line=''
fi
else
if [ "$bad_artist" == '' ]
then
bad_artist="$artist"
bad_line="$line"
else
# first, last and bad are the same artist :(
bad_result="$bad_result""$line"
fi
fi
done < <(shuf playlist)
# leftovers?
if [ "$bad_artist" != '' ]
then
bad_result="$bad_result""$bad_line"
fi
echo -n "$result"
echo -n "$bad_result"
Это могло быть более умно... в Вашем примере John, John будет обычно придерживаться того, чтобы быть last_artist, потому что это всегда пытается добавить first_artist сначала. Таким образом, если это получает двух других промежуточных художников, не достаточно умно добавить то к началу и другому в конец для ухода от тройного Джона. Таким образом со списками, которые в основном требуют, чтобы любым художником был John, Вы получаете больше отказов, чем Вы должны.