आपके उदाहरण डेटा और बाधाएं वास्तव में केवल कुछ समाधानों की अनुमति देती हैं - उदाहरण के लिए, आपको जॉन बी को हर दूसरे गीत को खेलना चाहिए। मैं यह मानने जा रहा हूं कि आपकी वास्तविक पूर्ण प्लेलिस्ट अनिवार्य रूप से जॉन बी नहीं है, इसे तोड़ने के लिए यादृच्छिक अन्य सामान के साथ ।
यह एक और यादृच्छिक दृष्टिकोण है। @ Frostschutz के समाधान के विपरीत, यह जल्दी से चलता है। हालाँकि, यह आपके मापदंड से मेल खाने वाले परिणाम की गारंटी नहीं देता है। मैं एक दूसरा दृष्टिकोण भी प्रस्तुत करता हूं, जो आपके उदाहरण डेटा पर काम करता है - लेकिन मुझे संदेह है कि आपके वास्तविक डेटा पर खराब परिणाम होंगे। आपका वास्तविक डेटा (obfuscated) होने के बाद, मैं दृष्टिकोण 3 को जोड़ता हूं - जो एक समान यादृच्छिक है, सिवाय इसके कि एक पंक्ति में एक ही कलाकार द्वारा दो गाने से बचा जाए। ध्यान दें कि यह केवल शेष गीतों के "डेक" में 5 "ड्रॉ" करता है, अगर इसके बाद भी इसे डुप्लिकेट कलाकार के साथ सामना करना पड़ता है, तो यह उस गाने को वैसे भी आउटपुट देगा - इस तरह, इसकी गारंटी है कि कार्यक्रम वास्तव में खत्म हो जाएगा।
दृष्टिकोण १
मूल रूप से, यह प्रत्येक बिंदु पर एक प्लेलिस्ट बनाता है, यह पूछते हुए कि "मेरे पास अभी भी कौन से कलाकार गाने हैं?" फिर एक यादृच्छिक कलाकार चुनना, और अंत में उस कलाकार का एक यादृच्छिक गीत। (अर्थात, प्रत्येक कलाकार को समान रूप से वेट किया जाता है, गानों की संख्या के अनुपात में नहीं।)
इसे अपनी वास्तविक प्लेलिस्ट पर आज़माएं, और देखें कि क्या यह समान रूप से यादृच्छिक की तुलना में बेहतर परिणाम देता है।
उपयोग:./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};
}
दृष्टिकोण २
एक दूसरे दृष्टिकोण के रूप में, एक यादृच्छिक कलाकार को चुनने के बजाय , आप सबसे अधिक गाने वाले कलाकार को चुन सकते हैं, जो कि अंतिम कलाकार भी नहीं है जिसे हमने चुना है । कार्यक्रम का अंतिम पैराग्राफ तब बन जाता है:
# 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};
}
कार्यक्रम का बाकी हिस्सा वही रहता है। ध्यान दें कि यह अब तक ऐसा करने का सबसे कारगर तरीका नहीं है, लेकिन यह किसी भी साने के आकार के प्लेलिस्ट के लिए पर्याप्त तेज़ होना चाहिए। आपके उदाहरण डेटा के साथ, सभी जेनरेट किए गए प्लेलिस्ट जॉन बी गीत, फिर अन्ना ए गीत, फिर जॉन बी गीत के साथ शुरू होंगे। उसके बाद, यह बहुत कम अनुमानित है (सभी के रूप में लेकिन जॉन बी के पास एक गीत बचा है)। ध्यान दें कि यह पर्ल 5.7 या इसके बाद का है।
दृष्टिकोण ३
उपयोग पिछले 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);
}