ข้อมูลและข้อ จำกัด ตัวอย่างของคุณจริง ๆ แล้วอนุญาตเพียงโซลูชันสองสามอย่างเท่านั้น - คุณต้องเล่น 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);
}