Örnek verileriniz ve kısıtlamalarınız aslında sadece birkaç çözüme izin verir; örneğin, John B.'yi diğer tüm şarkılarda çalmanız gerekir. Gerçek tam çalma listenizin aslında John B olmadığını ve onu parçalayacak rastgele başka şeylerle olduğunu varsayacağım .
Bu başka bir rastgele yaklaşımdır. @ Frostschutz'un çözümünün aksine, hızlı çalışır. Ancak kriterlerinize uygun bir sonuç garanti etmez. Ayrıca, örnek verilerinizde çalışan ikinci bir yaklaşım da sunuyorum - ancak gerçek verilerinizde kötü sonuçlar üreteceğinden şüpheleniyorum. Gerçek verilerinize (gizlenmiş) sahipken, aynı sanatçı tarafından üst üste iki şarkıdan kaçınması dışında, üniform bir rastgele olan yaklaşım 3'ü ekliyorum. Kalan şarkıların "destesine" sadece 5 "çekim" yaptığını unutmayın, bundan sonra yine de yinelenen bir sanatçı ile karşı karşıya kalırsa, bu şarkıyı yine de çıkarır - bu şekilde, programın gerçekten biteceğini garanti eder.
Yaklaşım 1
Temel olarak, her noktada "hala hangi şarkılardan çalmadığım şarkılar var?" Sorusunu soran bir çalma listesi oluşturur. Sonra rastgele bir sanatçı ve son olarak o sanatçıdan rastgele bir şarkı seç. (Yani, her sanatçı, şarkı sayısıyla orantılı değil, eşit ağırlıktadır.)
Gerçek oynatma listenizde bir deneyin ve tekdüze rastgele olduğundan daha iyi sonuçlar üretip üretmediğine bakın.
Kullanımı:./script-file < input.m3u > output.m3u
chmod +x
Tabii ki emin olun . Bazı M3U dosyalarının üstündeki imza satırını düzgün işlemediğini unutmayın ... ancak örneğinizde bu yoktu.
#!/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};
}
Yaklaşım 2
İkinci bir yaklaşım olarak, rastgele bir sanatçı seçmek yerine , en çok şarkıyı seçen sanatçıyı kullanabilirsiniz ; Programın son paragrafı daha sonra:
# 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};
}
Programın geri kalanı aynı kalır. Bunun bunu yapmanın en etkili yolu olmadığını, ancak herhangi bir aklı başında olan oynatma listeleri için yeterince hızlı olması gerektiğini unutmayın. Örnek verilerinizle, oluşturulan tüm çalma listeleri bir John B. şarkısı, sonra bir Anna A. şarkısı, sonra bir John B. şarkısı ile başlayacaktır. Bundan sonra, daha az tahmin edilebilir (John B. dışındaki herkesin bir şarkısı kaldığı için). Bunun Perl 5.7 veya üstü olduğunu varsaydığını unutmayın.
Yaklaşım 3
Kullanım önceki 2 ile aynıdır. Parçayı not edin 0..4
, 5 denemenin maks. Deneme sayısını artırabilirsiniz, örneğin, 0..9
toplam 10 verir. ( 0..4
= 0, 1, 2, 3, 4
, fark edeceğiniz aslında 5 öğedir).
#!/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);
}