Contoh data dan batasan Anda sebenarnya hanya memungkinkan beberapa solusi β Anda harus memainkan John B. setiap lagu lainnya, misalnya. Saya akan menganggap daftar putar lengkap Anda yang sebenarnya pada dasarnya bukan John B, dengan hal-hal acak lainnya untuk dipecah .
Ini adalah pendekatan acak lain. Tidak seperti solusi @ frostschutz, ini berjalan dengan cepat. Namun, itu tidak menjamin hasil yang cocok dengan kriteria Anda. Saya juga menyajikan pendekatan kedua, yang bekerja pada data contoh Anda β tetapi saya curiga akan menghasilkan hasil yang buruk pada data Anda yang sebenarnya. Memiliki data asli Anda (dikaburkan), saya menambahkan pendekatan 3 β yang merupakan acak seragam, kecuali itu menghindari dua lagu oleh artis yang sama berturut-turut. Perhatikan bahwa itu hanya membuat 5 "menarik" ke dalam "dek" dari lagu yang tersisa, jika setelah itu masih dihadapkan dengan artis duplikat, itu akan tetap menghasilkan lagu itu β dengan cara ini, dijamin bahwa program akan benar-benar selesai.
Pendekatan 1
Pada dasarnya, ini menghasilkan daftar putar pada setiap titik, menanyakan "dari artis mana saya masih memiliki lagu yang belum diputar?" Kemudian memilih artis acak, dan akhirnya lagu acak dari artis itu. (Artinya, masing-masing artis diberi bobot yang sama, tidak sebanding dengan jumlah lagu.)
Cobalah daftar putar Anda yang sebenarnya, dan lihat apakah itu menghasilkan hasil yang lebih baik daripada acak yang seragam.
Penggunaan:./script-file < input.m3u > output.m3u
Pastikan untuk chmod +x
itu, tentu saja. Catatan itu tidak menangani garis tanda tangan yang ada di bagian atas beberapa file M3U dengan benar ... tetapi contoh Anda tidak memilikinya.
#!/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};
}
Pendekatan 2
Sebagai pendekatan kedua, alih-alih memilih artis acak , Anda dapat menggunakan memilih artis dengan lagu terbanyak, yang juga bukan artis terakhir yang kami pilih . Paragraf akhir program kemudian menjadi:
# 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};
}
Sisa program tetap sama. Perhatikan bahwa ini sejauh ini bukan cara yang paling efisien untuk melakukan ini, tetapi harus cukup cepat untuk daftar putar dengan ukuran waras apa pun. Dengan data contoh Anda, semua daftar putar yang dihasilkan akan mulai dengan lagu John B., kemudian lagu Anna A., lalu lagu John B. Setelah itu, itu jauh lebih mudah diprediksi (karena semua orang kecuali John B. memiliki satu lagu yang tersisa). Perhatikan bahwa ini mengasumsikan Perl 5.7 atau lebih baru.
Pendekatan 3
Penggunaannya sama dengan 2. sebelumnya. Perhatikan 0..4
bagiannya, dari situlah 5 mencoba maks berasal. Anda dapat meningkatkan jumlah percobaan, misalnya, 0..9
akan memberikan 10 total. ( 0..4
= 0, 1, 2, 3, 4
, yang akan Anda perhatikan sebenarnya adalah 5 item).
#!/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);
}