예제 데이터 및 제약 조건은 실제로 몇 가지 솔루션 만 허용합니다. 예를 들어 다른 노래마다 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);
}