कुछ अतिरिक्त बाधाओं के साथ बेतरतीब ढंग से फ़ाइल फेरबदल करें


12

मेरे पास एक विशाल संगीत प्लेलिस्ट है और कुछ कलाकारों के पास कई एल्बम हैं, जबकि अन्य में सिर्फ एक गाना है। मैं प्लेलिस्ट को सॉर्ट करना चाहता था ताकि एक ही कलाकार एक पंक्ति में दो बार नहीं बजाए, या उसके गाने ज्यादातर प्लेलिस्ट के शुरुआत या अंत में समाप्त नहीं होंगे।

उदाहरण प्लेलिस्ट:

$ cat /tmp/playlist.m3u
Anna A. - Song 1
Anna A. - Song 2
I--Rock - Song 1
John B. - Song 1
John B. - Song 2
John B. - Song 3
John B. - Song 4
John B. - Song 5
Kyle C. - Song 1
U--Rock - Song 1

से उत्पादन sort -Rया shuf:

$ sort -R /tmp/playlist.m3u
Anna A. - Song 1 #
U--Rock - Song 1
Anna A. - Song 2 # Anna's songs are all in the beginning.
John B. - Song 2
I--Rock - Song 1
John B. - Song 1
Kyle C. - Song 1
John B. - Song 4 #
John B. - Song 3 #
John B. - Song 5 # Three of John's songs in a row.

मैं क्या उम्मीद कर रहा हूँ:

$ some_command /tmp/playlist.m3u
John B. - Song 1
Anna A. - Song 1
John B. - Song 2
I--Rock - Song 1
John B. - Song 3
Kyle C. - Song 1
Anna A. - Song 2
John B. - Song 4
U--Rock - Song 1
John B. - Song 5

13
तकनीकी रूप से, आप जो पूछ रहे हैं वह कम यादृच्छिकता है, और अधिक संरचना है। यह असंभव नहीं है, लेकिन इसके लिए एक (bash / awk / perl / python / etc) स्क्रिप्ट की आवश्यकता होगी।
गोल्डीलॉक्स

या एक संरचित यादृच्छिकता :)
टेरेसा ई जूनियर

बिल्कुल सही! यह पर्ल या अजगर में एक अच्छा व्यायाम होगा। मुझे लगता है कि यह बैश के साथ सिरदर्द होगा, हालांकि यह awk के साथ अच्छी तरह से काम कर सकता है - मुझे नहीं पता कि कहने के लिए पर्याप्त अच्छी तरह से awk है।
गोल्डीलॉक्स

चूँकि ऐसा करने के लिए कोई टूल नहीं लगता है, लिपि को जाने का रास्ता लगता है। ऐसा नहीं है कि मैं आलसी हूं, लेकिन मैं विचारों से बाहर हूं।
टेरेसा ई जूनियर

1
आप एक सरल एल्गोरिथ्म के साथ ऐसा करने में सक्षम हो सकते हैं: प्रत्येक कलाकार द्वारा बदले में एक यादृच्छिक गीत का चयन करके प्लेलिस्ट बनाएं (जहां बारी यादृच्छिक रूप से भी लेकिन कलाकार पुनरावृत्ति के बिना हो सकती है)। जब एक कलाकार के सभी गाने समाप्त हो गए हैं, तो शेष कलाकारों द्वारा गाने को फिर से गाना शुरू करें (फिर बारी-बारी से उनके बीच बारी-बारी से) मौजूदा प्लेलिस्ट के साथ इस तरह से एक ही कलाकार द्वारा गाने के आसन्नता को कम करें। जब तक आप कर रहे हैं दोहराते रहें। मुझे खेद है कि मेरे पास इसे वास्तविक स्क्रिप्ट में पैन करने का समय नहीं है; मैंने सिर्फ यह सोचा कि आपके रोल को रोल करने में आपकी मदद करना उपयोगी हो सकता है।
जोसेफ आर।

जवाबों:


5

अगर मुझे कार्ड बजाने के डेक पर उस फेरबदल को लागू करना होता है, तो मुझे लगता है कि मैं पहले डेक को फेरबदल करूंगा, फिर अपनी आंखों के सामने और बाईं तरफ से प्रसंस्करण से पहले, जहां भी आसन्न क्लब या दिल हों, वहां कार्ड प्रदर्शित करें। सभी को स्थानांतरित करें, लेकिन उनमें से एक यादृच्छिक रूप से कहीं और (हालांकि एक ही प्रकार के दूसरे के बगल में नहीं)।

उदाहरण के लिए, जैसे हाथ से

🂡 🂢 🂣 🂤 🂥 🂦 🂧 🂨 🂱 🂲 🂳 🃁 🃂 🃃 🃑 🃒

बुनियादी फेरबदल के बाद:

🂣 🃑 🂲 🂦 🂳 🃁<🂧 🂡 🂨>🃂<🂤 🂢>🃃 🂱 🂥 🃒
                   1  2       3

आसन्न हुकुमों के दो समूह, हमें 1, 2 और 3 को स्थानांतरित करने की आवश्यकता है। 1 के लिए, विकल्प हैं:

🂣 🃑 🂲 🂦 🂳 🃁 🂧 🂡 🂨 🃂 🂤 🂢 🃃 🂱 🂥 🃒
    ↑        ↑                    ↑        ↑

हम उन 4 में से एक को यादृच्छिक रूप से चुनते हैं। फिर हम 2 और 3 के लिए प्रक्रिया दोहराते हैं।

perlउस में लागू किया जाएगा:

shuf list | perl -e '
  @songs = map {/(.*?)-/; [$1,$_]} <>;
  for ($i = 0; $i < @songs; $i++) {
    if (($author = $songs[$i]->[0]) eq $previous) {
      my @reloc_candidates, $same;
      for($j = 0; $j < @songs; $j++) {
        # build a list of positions where we could move that song to
        if ($songs[$j]->[0] eq $author) {$same = 1} else {
          push @reloc_candidates, $j unless $same;
          $same = 0;
        }
      }
      push @reloc_candidates, $j unless $same;

      if (@reloc_candidates) {
        # now pick one of them at random:
        my $chosen = $reloc_candidates[int(rand(@reloc_candidates))];
        splice @songs, $chosen - ($chosen > $i), 0, splice @songs, $i, 1;
        $i -= $chosen > $i;
      }
    }
    $previous = $author;
  }
  print map {$_->[1]} @songs'

यह गैर-आसन्न कलाकारों के साथ एक समाधान ढूंढेगा यदि यह मौजूद है (जब तक कि आधे से अधिक गाने एक ही कलाकार से न हों), और एक समान AFAICT होना चाहिए।


तीन अलग-अलग लिपियों (पर्ल और बैश) की कोशिश करने पर, उन सभी ने प्लेलिस्ट को किनारे कर दिया जो मैंने बगल के गीतों पर छोड़ दिया था, लेकिन आप इसे अधिक स्मार्ट तरीके से कर रहे हैं। इसके अलावा, केवल आपका जॉन बी उदाहरण पर पूरी तरह से काम करता है , जो निस्संदेह एक सर्वोत्तम उत्तर के लिए बनाता है। मैंने उसके उत्तर को स्वीकार करने के लिए अपमानजनक वादा किया, क्योंकि वह इतना धैर्यवान और मेरे लिए मददगार था, और उसका तीसरा दृष्टिकोण भी बहुत अच्छा है। तो मैं आपको उसके लिए सबसे अच्छा जवाब और इनाम दूंगा, और मुझे आशा है कि वह मुझ पर गुस्सा नहीं होगा :)
टेरेसा ई जूनियर

7

आपके उदाहरण डेटा और बाधाएं वास्तव में केवल कुछ समाधानों की अनुमति देती हैं - उदाहरण के लिए, आपको जॉन बी को हर दूसरे गीत को खेलना चाहिए। मैं यह मानने जा रहा हूं कि आपकी वास्तविक पूर्ण प्लेलिस्ट अनिवार्य रूप से जॉन बी नहीं है, इसे तोड़ने के लिए यादृच्छिक अन्य सामान के साथ

यह एक और यादृच्छिक दृष्टिकोण है। @ 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);
}

@TeresaeJunior क्या आपने वास्तविक डेटा पर दो कार्यक्रमों की कोशिश की, और देखें कि क्या आपकी पसंद है? (और, वाह, यह देखते हुए, यह बहुत "Fhk Hhck" भारी है ... मैं एक दृष्टिकोण 3 जोड़ने जा रहा हूं)
derobert

कुछ कलाकार वास्तव में एक पंक्ति में दो बार खेलते हैं (आप इसे देख सकते हैं sed 's/ - .*//' output.m3u | uniq -d)। और क्या आप यह समझा सकते हैं कि अगर यह कुछ कलाकारों की शुरुआत या अंत में प्लेलिस्ट का अंत नहीं करता है?
टेरेसा ई जूनियर

दृष्टिकोण 1 वास्तव में एक पंक्ति में दो (या अधिक) की अनुमति देता है। दृष्टिकोण 2 नहीं है। दृष्टिकोण 3 (इसे संपादित करने के बारे में) भी (अच्छी तरह से, ज्यादातर) नहीं करता है। दृष्टिकोण 2 निश्चित रूप से सबसे आम कलाकारों द्वारा प्लेलिस्ट की शुरुआत को मापता है। दृष्टिकोण 3 नहीं होगा।
derobert

1
@TeresaeJunior मुझे खुशी है कि तीसरे ने काम किया! मुझे यकीन है कि वास्तव में क्या दृष्टिकोण 4 हो गया होता नहीं कर रहा हूँ, लेकिन यह डरावना हो जाएगा ...
derobert

1
@JosephR। दृष्टिकोण # 3 एक यादृच्छिक गीत चुनकर प्रत्येक कलाकार द्वारा वजन के रूप में गाने की संख्या का उपयोग करता है । एक कलाकार के पास जितने अधिक गाने होते हैं, उतने अधिक कलाकार को लेने की संभावना होती है। # 1 एकमात्र ऐसा गाना है जो गाने की संख्या से वजन नहीं बढ़ाता है।
derobert

2

अगर आपको बुरा नहीं लगता तो यह बेहद अक्षम है ...

while [ 1 ]
do
    R="`shuf playlist`"
    D="`echo "$R" | sed -e 's/ - .*//' | uniq -c -d`"
    if [ "$D" == "" ]
    then
        break
    #else # DEBUG ONLY:
    #    echo --- FAIL: ---
    #    echo "$D"
    #    echo -------------
    fi
done

echo "$R"

यह केवल तब तक लुढ़कता और लुढ़कता रहता है जब तक कि यह एक परिणाम नहीं हो जाता है जिसमें एक पंक्ति में दो या अधिक जॉन्स नहीं होते हैं। यदि आपकी प्लेलिस्ट में बहुत सारे जॉन हैं तो ऐसा कोई संयोजन मौजूद नहीं है या इसे लुढ़का होने की संभावना नहीं है, ठीक है, यह लटकाएगा।

उदाहरण आपके इनपुट के साथ:

John B. - Song 4
Kyle C. - Song 1
Anna A. - Song 2
John B. - Song 3
Anna A. - Song 1
John B. - Song 1
U--Rock - Song 1
John B. - Song 2
I--Rock - Song 1
John B. - Song 5

यदि आप डिबग लाइनों को असहज करते हैं, तो यह आपको बताएगा कि यह विफल क्यों हुआ:

--- FAIL: ---
      3 John B.
-------------
--- FAIL: ---
      2 John B.
      2 John B.
-------------

यदि यह अनिश्चित काल तक लटका रहता है तो इसके कारण को निर्धारित करने में मदद करनी चाहिए।


मुझे यह विचार पसंद है, लेकिन स्क्रिप्ट लगभग 15 मी के लिए चल रही है और एक उपयुक्त संयोजन नहीं मिल सका है। ऐसा नहीं है कि मेरे पास जॉन द्वारा बहुत सारे गाने हैं, लेकिन प्लेलिस्ट 7000 से अधिक लाइनें हैं, और ऐसा लगता है कि कैसे sortडिज़ाइन किया गया है।
टेरेसा ई जूनियर

1
प्रदर्शन के संबंध में, shufप्लेलिस्ट को 80 गुना तेजी से बदल देता है sort -R। मुझे नहीं पता था कि या तो! मैं इसे 15 मिनट तक छोड़ दूंगा shuf, संभावना अधिक है!
टेरेसा ई जूनियर

डिबग करने के लिए, echo "$D"से पहले if। आपको यह बताना चाहिए कि कौन से डुप्लिकेट ने परिणाम को चुने जाने से रोका। आपको यह बताना चाहिए कि समस्या की तलाश कहाँ करें। (संपादित करें: उत्तर के लिए संभव डिबग कोड जोड़ा गया है।)
फ्रॉस्ट्सचुट्ज़ 20

DEBUG हमेशा लगभग 100 लाइनें दिखाता है, लेकिन यादृच्छिक कलाकारों से, इसलिए ऐसा लगता है कि बहुत सारे कलाकार समस्या का कारण बन रहे हैं। मुझे लगता है कि यह वास्तव में sortया के साथ संभव नहीं है shuf
टेरेसा ई जूनियर

1

बैश का उपयोग कर एक और दृष्टिकोण। यह यादृच्छिक क्रम में प्लेलिस्ट को पढ़ता है, सूची के दूसरे छोर पर लाइन डालने की कोशिश करता है यदि यह एक डुप्लिकेट है, और इसे किसी अन्य स्थान पर पुन: स्थापित करने के लिए एक एकल दुपट्टा डालता है। यह विफल रहता है अगर ट्रिपल डुप्लिकेट (पहले, अंतिम, और समान रूप से सेट) हैं और यह सूची के बहुत अंत तक उन खराब प्रविष्टियों को जोड़ देगा। यह उस व्यापक सूची को हल करने में सक्षम प्रतीत होता है जिसे आपने ज्यादातर समय अपलोड किया था।

#!/bin/bash

first_artist=''
last_artist=''
bad_artist=''
bad_line=''
result=''
bad_result=''

while read line
do
    artist=${line/ - */}
    line="$line"$'\n'

    if [ "$artist" != "$first_artist" ]
    then
        result="$line""$result"
        first_artist="$artist"

        # special case: first = last
        if [ "$last_artist" == '' ]
        then
            last_artist="$artist"
        fi

        # try reinserting bad
        if [ "$bad_artist" != '' -a "$bad_artist" != "$first_artist" ]
        then
            first_artist="$bad_artist"
            result="$bad_line""$result"
            bad_artist=''
            bad_line=''
        fi
    elif [ "$artist" != "$last_artist" ]
    then
        result="$result""$line"
        last_artist="$artist"

        # try reinserting bad
        if [ "$bad_artist" != '' -a "$bad_artist" != "$last_artist" ]
        then
            last_artist="$bad_artist"
            result="$result""$bad_line"
            bad_artist=''
            bad_line=''
        fi
    else
        if [ "$bad_artist" == '' ]
        then
            bad_artist="$artist"
            bad_line="$line"
        else
            # first, last and bad are the same artist :(
            bad_result="$bad_result""$line"
        fi
    fi
done < <(shuf playlist)

# leftovers?
if [ "$bad_artist" != '' ]
then
    bad_result="$bad_result""$bad_line"
fi

echo -n "$result"
echo -n "$bad_result"

यह होशियार हो सकता है ... आपके जॉन उदाहरण में, जॉन आमतौर पर last_artist होने के लिए छड़ी करेंगे क्योंकि यह हमेशा पहले_कार्टिस्ट को जोड़ने की कोशिश करता है। इसलिए अगर इसे बीच में दो अन्य कलाकार मिलते हैं, तो यह शुरुआत और अंत तक ट्रिपल-जॉन से बचने के लिए एक को जोड़ने के लिए पर्याप्त स्मार्ट नहीं है। तो उन सूचियों के साथ जिन्हें मूल रूप से जॉन होने के लिए हर दूसरे कलाकार की आवश्यकता होती है, आपको अपनी तुलना में अधिक विफलताएं मिलनी चाहिए।


इस बैश स्क्रिप्ट के लिए धन्यवाद। यह केवल एक है जिसे मैं वास्तव में समझ सकता हूं और इच्छाशक्ति को संशोधित कर सकता हूं!
टेरेसा ई जूनियर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.