Dữ liệu ví dụ và các ràng buộc của bạn thực sự chỉ cho phép một vài giải pháp mà bạn phải chơi John B. mọi bài hát khác chẳng hạn. Tôi sẽ giả sử danh sách phát đầy đủ thực tế của bạn không phải là John B, với những thứ khác ngẫu nhiên để phá vỡ nó .
Đây là một cách tiếp cận ngẫu nhiên khác. Không giống như giải pháp của @ frostschutz, nó chạy rất nhanh. Tuy nhiên, nó không đảm bảo một kết quả phù hợp với tiêu chí của bạn. Tôi cũng trình bày một cách tiếp cận thứ hai, hoạt động trên dữ liệu mẫu của bạn nhưng tôi nghi ngờ sẽ tạo ra kết quả xấu trên dữ liệu thực của bạn. Có dữ liệu thực của bạn (bị xáo trộn), tôi thêm cách tiếp cận 3, đây là một cách ngẫu nhiên thống nhất, ngoại trừ việc nó tránh hai bài hát của cùng một nghệ sĩ liên tiếp. Lưu ý rằng nó chỉ tạo ra 5 "bản vẽ" trong "bộ bài" còn lại, nếu sau đó nó vẫn phải đối mặt với một nghệ sĩ trùng lặp, thì dù sao đi nữa, bài hát này sẽ đảm bảo rằng chương trình sẽ thực sự kết thúc.
Cách tiếp cận 1
Về cơ bản, nó tạo ra một danh sách nhạc theo từng thời điểm, hỏi "những nghệ sĩ nào tôi vẫn có những bài hát chưa được phát từ đó?" Sau đó chọn một nghệ sĩ ngẫu nhiên, và cuối cùng là một bài hát ngẫu nhiên từ nghệ sĩ đó. (Nghĩa là, mỗi nghệ sĩ đều có trọng số như nhau, không tương xứng với số lượng bài hát.)
Hãy dùng thử danh sách phát thực tế của bạn và xem liệu nó có mang lại kết quả tốt hơn so với ngẫu nhiên thống nhất không.
Cách sử dụng:./script-file < input.m3u > output.m3u
Hãy chắc chắn với chmod +x
nó, tất nhiên. Lưu ý rằng nó không xử lý dòng chữ ký nằm ở đầu một số tệp M3U đúng cách ... nhưng ví dụ của bạn không có điều đó.
#!/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};
}
Cách tiếp cận 2
Như một cách tiếp cận thứ hai, thay vì chọn một nghệ sĩ ngẫu nhiên , bạn có thể sử dụng chọn nghệ sĩ có nhiều bài hát nhất, người cũng không phải là nghệ sĩ cuối cùng chúng tôi chọn . Đoạn cuối của chương trình sau đó trở thành:
# 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};
}
Phần còn lại của chương trình vẫn giữ nguyên. Lưu ý rằng điều này cho đến nay không phải là cách hiệu quả nhất để làm điều này, nhưng nó phải đủ nhanh cho danh sách phát ở bất kỳ kích thước lành mạnh nào. Với dữ liệu mẫu của bạn, tất cả các danh sách phát được tạo sẽ bắt đầu bằng một bài hát John B., sau đó là bài hát Anna A., sau đó là bài hát John B. Sau đó, điều đó ít được dự đoán hơn (như mọi người trừ John B. chỉ còn một bài hát). Lưu ý rằng điều này giả định Perl 5.7 trở lên.
Cách tiếp cận 3
Cách sử dụng giống như trước 2. Lưu ý 0..4
phần, đó là nơi 5 lần thử tối đa đến từ. Bạn có thể tăng số lần thử, ví dụ: 0..9
sẽ cho tổng số 10 lần. ( 0..4
= 0, 1, 2, 3, 4
, mà bạn sẽ nhận thấy thực sự là 5 mục).
#!/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);
}