Файл перестановки случайным образом с некоторыми дополнительными ограничениями

Если у Вас есть корневой доступ к серверу, простой способ решить такие проблемы состоит в том, чтобы выполнить 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 вместо этого.)

12
01.01.2017, 11:41
4 ответа

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

Например, рукой как

                               

После основной перестановки:

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

5
27.01.2020, 19:55
  • 1
    После попытки трех различных сценариев (жемчуг и удар), все они переставляют плей-лист, который я оставил на pastebin, не оставляя смежные песни, но ваши, кажется, делают это более умным способом. Кроме того, только ваши работают отлично над примером John B., который, несомненно, делает его для лучшего ответа. Я обещал derobert принять его ответ, так как он был так терпелив и полезен мне, и его 3-й подход очень хорош также. Таким образом, я дам Вам лучший ответ и щедрость ему, и я надеюсь, что он не становится сердитым на меня :) –  Teresa e Junior 06.11.2013, 01:55

Ваши данные в качестве примера и ограничения на самом деле только позволяют несколько решений — необходимо играть John B. любая песня, например. Я собираюсь предположить, что Ваш фактический полный плей-лист не по существу John B со случайным другим материалом для разбивания его.

Это - другой случайный подход. В отличие от решения @frostschutz, это работает быстро. Это не гарантирует результат, который соответствует Вашим критериям, как бы то ни было. Я также представляю второй подход, который работает над Вашими данными в качестве примера — но я подозреваю, приведет к плохим результатам на Ваших реальных данных. Запутывая Ваши реальные данные, я добавляю подход 3 — который является случайной универсальной формой, кроме него избегает двух песен тем же художником подряд. Обратите внимание, что это только делает 5, "тянет" в "деку" остающихся песен, если после этого это все еще столкнется с дублирующимся художником, то это произведет ту песню так или иначе — этот путь, ее гарантируемый, который на самом деле закончит программа.

Подход 1

В основном это генерирует плей-лист в каждой точке, спрашивая, "от каких художников у меня все еще есть неиграемые песни?" Затем выбирая случайного художника и наконец случайную песню от того художника. (Таким образом, каждый художник взвешивается одинаково, не в пропорции к количеству песен.)

Дайте ему попытку на своем фактическом плей-листе и посмотрите, приводит ли это к лучшим результатам, чем однородно случайный.

Использование: ./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};
}

Подход 2

Как второй подход, вместо выбора случайный художник, которого можно использовать, выбирает художника с большинством песен, который является также не последним художником, которого мы выбрали. Заключительный абзац программы затем становится:

# 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 или позже.

Подход 3

Использование совпадает с предыдущими 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);
}
7
27.01.2020, 19:55
  • 1
    @TeresaeJunior Вы пробовали эти две программы на фактических данных и видели, ли любой к Вашей симпатии? (И, ничего себе, смотря, который, это - очень тяжелый "Fhk Hhck"... Я собираюсь добавить подход 3) –  derobert 04.11.2013, 19:45
  • 2
    Некоторые художники на самом деле играют дважды подряд (можно проверить его с sed 's/ - .*//' output.m3u | uniq -d). И Вы могли объяснить, заботится ли это о некоторых художниках, не заканчивающих в начале или конце плей-листа? –  Teresa e Junior 04.11.2013, 19:50
  • 3
    Подход 1 действительно позволяет два (или больше) подряд. Подход 2 не делает. Приблизьтесь 3 (собирающийся редактирование это в), также не делает (хорошо, главным образом). Приблизьтесь 2, определенно взвешивает начало плей-листа наиболее распространенных художников. Подход 3 не будет. –  derobert 04.11.2013, 19:52
  • 4
    @TeresaeJunior Паники Ядра, я рад, что третий работал! Я не уверен точно, что приближается 4, был бы, но это будет страшно... –  derobert 04.11.2013, 20:29
  • 5
    @JosephR. Подход № 3 действительно использует количество песен каждым художником как вес — неявно путем выбора случайной песни. Чем больше песен художник имеет, тем более вероятно, что художник должен быть выбран. № 1 является единственным, который не взвешивает количеством песен. –  derobert 04.11.2013, 23:33

Если Вы не возражаете против него являющийся ужасно неэффективным...

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

Это должно помочь определить причину в случае, если она зависает неограниченно долго.

2
27.01.2020, 19:55
  • 1
    мне нравится идея, но сценарий работал почти за 15 м и не мог найти подходящую комбинацию. Не то, чтобы у меня есть слишком много песен John, но плей-лист является больше чем 7 000 строк, и это, кажется, как sort разработан. –  Teresa e Junior 01.11.2013, 21:53
  • 2
    Относительно производительности, shuf переставляет плей-лист в 80 раз быстрее, чем sort -R. Я не знал это также! Я оставлю его работающий в течение 15 минут с shuf, возможности выше! –  Teresa e Junior 01.11.2013, 21:57
  • 3
    Отлаживать, echo "$D" перед if. Это должно сказать Вам, какие дубликаты предотвратили результат того, чтобы быть выбранным. Это должно сказать Вам, где искать проблему. (Редактирование: Добавленная возможная отладка кодирует к ответу.) –  frostschutz 01.11.2013, 22:07
  • 4
    ОТЛАДЬТЕ всегда показывает приблизительно 100 строк, но от случайных художников, таким образом, это кажется, много художников вызывает проблему. Я думаю, что это не действительно возможно с 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, Вы получаете больше отказов, чем Вы должны.

1
27.01.2020, 19:55
  • 1
    Спасибо за этот сценарий удара. Это - единственное, которое я могу действительно понять и изменить по желанию! –  Teresa e Junior 06.11.2013, 01:57

Теги

Похожие вопросы