क्या किसी श्रेणी में N यादृच्छिक पूर्णांकों को उत्पन्न करने का एक कुशल तरीका है जिसमें एक दी गई राशि या औसत है?


11

क्या एन पूर्णांकों का एक यादृच्छिक संयोजन उत्पन्न करने का एक कुशल तरीका है-

  • प्रत्येक पूर्णांक अंतराल में है [ min, max],
  • पूर्णांकों का योग है sum,
  • पूर्णांक किसी भी क्रम (जैसे, यादृच्छिक क्रम), और में दिखाई दे सकते हैं
  • संयोजन सभी आवश्यकताओं के बीच समान रूप से यादृच्छिक रूप से चुना गया है जो अन्य आवश्यकताओं को पूरा करता है?

क्या यादृच्छिक संयोजनों के लिए एक समान एल्गोरिथ्म है जिसमें पूर्णांकों को उनके मूल्यों (बजाय किसी भी क्रम में) द्वारा क्रमबद्ध क्रम में दिखाई देना चाहिए?

(माध्य के साथ एक उपयुक्त संयोजन चुनना meanएक विशेष मामला है, यदि sum = N * mean। यह समस्या sumN भागों में एक समान यादृच्छिक विभाजन उत्पन्न करने के बराबर है जो प्रत्येक अंतराल में [ min, max] हैं और किसी भी क्रम में या उनके द्वारा क्रमबद्ध क्रम में प्रकट होते हैं। मान, जैसा भी मामला हो।)

मुझे पता है कि यादृच्छिक क्रम (EDIT [अप्रैल 27]: एल्गोरिथम संशोधित) में दिखाई देने वाले संयोजनों के लिए इस समस्या को निम्न तरीके से हल किया जा सकता है।

  1. अगर N * max < sum या N * min > sum, कोई समाधान नहीं है।

  2. यदि N * max == sum, केवल एक ही समाधान है, जिसमें सभी Nसंख्याएँ समान हैं max। यदि N * min == sum, केवल एक ही समाधान है, जिसमें सभी Nसंख्याएँ समान हैंmin

  3. स्मिथ और ट्रोम्बल में दिए गए एल्गोरिथ्म का उपयोग करें ("यूनिट सिम्प्लेक्स से नमूनाकरण", 2004) योग के साथ एन यादृच्छिक गैर-नकारात्मक पूर्णांक उत्पन्न करने के लिए।sum - N * min

  4. जोड़ना minइस तरह उत्पन्न प्रत्येक संख्या में ।

  5. यदि किसी संख्या से अधिक है max , चरण 3 पर जाएं।

हालाँकि, यह एल्गोरिथ्म धीमा है अगर maxइससे कम है sum। उदाहरण के लिए, मेरे परीक्षणों के अनुसार (ऊपर विशेष मामले के कार्यान्वयन के साथ)mean ), एल्गोरिदम औसतन अस्वीकार करता है-

  • के बारे में 1.6 नमूने अगर N = 7, min = 3, max = 10, sum = 42 , लेकिन
  • के बारे में 30.6 नमूने हैं N = 20, min = 3, max = 10, sum = 120

क्या ऊपर की आवश्यकताओं को पूरा करते हुए बड़े एन के लिए कुशल होने के लिए इस एल्गोरिदम को संशोधित करने का एक तरीका है?

संपादित करें:

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

  1. गणना करें X, दिए गए मान्य संयोजनों की संख्या sum,min और max
  2. चुनें Y , एक समान यादृच्छिक पूर्णांक [0, X)
  3. कन्वर्ट ("अनट्रैंक") Yएक वैध संयोजन में ।

हालाँकि, वैध संयोजनों (या क्रमपरिवर्तन) की संख्या की गणना करने के लिए एक सूत्र है, और क्या पूर्णांक को एक वैध संयोजन में बदलने का एक तरीका है? [संपादित (अप्रैल 28): संयोजन के बजाय क्रमपरिवर्तन के लिए समान]।

EDIT (अप्रैल 27):

Devroye की नॉन-यूनिफ़ॉर्म रैंडम वैरेट जनरेशन (1986) को पढ़ने के बाद , मैं इस बात की पुष्टि कर सकता हूं कि यह रैंडम पार्टीशन जनरेट करने की समस्या है। इसके अलावा, पेज 661 पर व्यायाम 2 (विशेषकर ई) इस प्रश्न के लिए प्रासंगिक है।

EDIT (अप्रैल 28):

जैसा कि मैंने दिया एल्गोरिथ्म ने कहा कि वर्दी एक समान है जहां शामिल पूर्णांक यादृच्छिक क्रम में दिए गए हैं , जैसा कि उनके मूल्यों द्वारा क्रमबद्ध क्रम के विपरीत है । चूंकि दोनों समस्याएं सामान्य रुचि की हैं, इसलिए मैंने इस प्रश्न को दोनों समस्याओं के लिए एक विहित उत्तर की तलाश के लिए संशोधित किया है।

निम्नलिखित रूबी कोड का उपयोग एकरूपता के लिए संभावित समाधान (जहां algorithm(...)उम्मीदवार एल्गोरिदम है) को सत्यापित करने के लिए किया जा सकता है :

combos={}
permus={}
mn=0
mx=6
sum=12
for x in mn..mx
  for y in mn..mx
    for z in mn..mx
      if x+y+z==sum
        permus[[x,y,z]]=0
      end
      if x+y+z==sum and x<=y and y<=z
        combos[[x,y,z]]=0
      end
    end
  end
end

3000.times {|x|
 f=algorithm(3,sum,mn,mx)
 combos[f.sort]+=1
 permus[f]+=1
}
p combos
p permus

EDIT (अप्रैल 29): वर्तमान कार्यान्वयन के रूबी कोड को फिर से जोड़ा।

रूबी में निम्नलिखित कोड उदाहरण दिया गया है, लेकिन मेरा प्रश्न प्रोग्रामिंग भाषा से स्वतंत्र है:

def posintwithsum(n, total)
    raise if n <= 0 or total <=0
    ls = [0]
    ret = []
    while ls.length < n
      c = 1+rand(total-1)
      found = false
      for j in 1...ls.length
        if ls[j] == c
          found = true
          break
        end
      end
      if found == false;ls.push(c);end
    end
    ls.sort!
    ls.push(total)
    for i in 1...ls.length
       ret.push(ls[i] - ls[i - 1])
    end
    return ret
end

def integersWithSum(n, total)
 raise if n <= 0 or total <=0
 ret = posintwithsum(n, total + n)
 for i in 0...ret.length
    ret[i] = ret[i] - 1
 end
 return ret
end

# Generate 100 valid samples
mn=3
mx=10
sum=42
n=7
100.times {
 while true
    pp=integersWithSum(n,sum-n*mn).map{|x| x+mn }
    if !pp.find{|x| x>mx }
      p pp; break # Output the sample and break
    end
 end
}

क्या आप अपनी तीसरी आवश्यकता स्पष्ट कर सकते हैं? क्या आपको सभी संभावित संयोजनों (गलत अर्थ वाले लोगों के साथ), या सभी वैध संयोजनों (सही अर्थ वाले लोगों के बीच) के बीच एकरूपता की आवश्यकता है ?
user58697

सभी वैध संयोजन, यानी सभी संयोजन जो अन्य आवश्यकताओं को पूरा करते हैं।
पीटर ओ।

यदि हमारे पास [पूर्णांक] में एन पूर्णांकों के लिए प्रतिबंधित राशि के विभाजन को गिनने और उन्मुक्त करने का एक तरीका है, तो क्या उन विभाजनों में से कोई एक यादृच्छिक रूप से चुनना और एक समान वितरण का प्रतिनिधित्व करेगा, और क्या यह आपके वर्तमान तरीके से अधिक कुशल होगा? योग और N कितने बड़े हो सकते हैं?
לעג ברקן

मुझे नहीं पता कि आपके "योग के विभाजन" से क्या अभिप्राय है, और मुझे इस प्रमाण के बारे में पता नहीं है कि ऐसा करने से इस प्रश्न के अर्थ में समान वितरण होता है। इस सवाल के लिए, दोनों sumऔर Nप्रभावी रूप से असीमित (कारण के भीतर) कर रहे हैं। मैं एक विहित उत्तर की तलाश कर रहा हूं क्योंकि स्टैक ओवरफ्लो पर पूछे गए कई प्रश्नों में अंतर्निहित समस्या पॉप अप होती है, जिसमें यह एक और यह भी शामिल है । @ גלעדברקן
पीटर ओ

यदि हम प्रत्येक संभावित संयोजन को "रैंक" (या इंडेक्स) देते हैं, तो उन सभी की एक व्यवस्थित व्यवस्था में, "अनरैंकिंग" का अर्थ होगा, संयोजन को उत्पन्न करना, इसकी रैंक (और एन, मिनट और अधिकतम, निश्चित रूप से)। सभी संभावित संयोजनों में से एक का ऐसा विकल्प एक समान वितरण के अनुरूप क्यों नहीं होगा?
לעג ברקן

जवाबों:


3

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

import java.util.Random;
import java.util.function.Supplier;

public abstract class PartitionGenerator implements Supplier<int[]>{
    public static final Random rand = new Random();
    protected final int numberCount;
    protected final int min;
    protected final int range;
    protected final int sum; // shifted sum
    protected final boolean sorted;

    protected PartitionGenerator(int numberCount, int min, int max, int sum, boolean sorted) {
        if (numberCount <= 0)
            throw new IllegalArgumentException("Number count should be positive");
        this.numberCount = numberCount;
        this.min = min;
        range = max - min;
        if (range < 0)
            throw new IllegalArgumentException("min > max");
        sum -= numberCount * min;
        if (sum < 0)
            throw new IllegalArgumentException("Sum is too small");
        if (numberCount * range < sum)
            throw new IllegalArgumentException("Sum is too large");
        this.sum = sum;
        this.sorted = sorted;
    }

    // Whether this generator returns sorted arrays (i.e. combinations)
    public final boolean isSorted() {
        return sorted;
    }

    public interface GeneratorFactory {
        PartitionGenerator create(int numberCount, int min, int max, int sum);
    }
}

import java.math.BigInteger;

// Permutations with repetition (i.e. unsorted vectors) with given sum
public class PermutationPartitionGenerator extends PartitionGenerator {
    private final double[][] distributionTable;

    public PermutationPartitionGenerator(int numberCount, int min, int max, int sum) {
        super(numberCount, min, max, sum, false);
        distributionTable = calculateSolutionCountTable();
    }

    private double[][] calculateSolutionCountTable() {
        double[][] table = new double[numberCount + 1][sum + 1];
        BigInteger[] a = new BigInteger[sum + 1];
        BigInteger[] b = new BigInteger[sum + 1];
        for (int i = 1; i <= sum; i++)
            a[i] = BigInteger.ZERO;
        a[0] = BigInteger.ONE;
        table[0][0] = 1.0;
        for (int n = 1; n <= numberCount; n++) {
            double[] t = table[n];
            for (int s = 0; s <= sum; s++) {
                BigInteger z = BigInteger.ZERO;
                for (int i = Math.max(0, s - range); i <= s; i++)
                    z = z.add(a[i]);
                b[s] = z;
                t[s] = z.doubleValue();
            }
            // swap a and b
            BigInteger[] c = b;
            b = a;
            a = c;
        }
        return table;
    }

    @Override
    public int[] get() {
        int[] p = new int[numberCount];
        int s = sum; // current sum
        for (int i = numberCount - 1; i >= 0; i--) {
            double t = rand.nextDouble() * distributionTable[i + 1][s];
            double[] tableRow = distributionTable[i];
            int oldSum = s;
            // lowerBound is introduced only for safety, it shouldn't be crossed 
            int lowerBound = s - range;
            if (lowerBound < 0)
                lowerBound = 0;
            s++;
            do
                t -= tableRow[--s];
            // s can be equal to lowerBound here with t > 0 only due to imprecise subtraction
            while (t > 0 && s > lowerBound);
            p[i] = min + (oldSum - s);
        }
        assert s == 0;
        return p;
    }

    public static final GeneratorFactory factory = (numberCount, min, max,sum) ->
        new PermutationPartitionGenerator(numberCount, min, max, sum);
}

import java.math.BigInteger;

// Combinations with repetition (i.e. sorted vectors) with given sum 
public class CombinationPartitionGenerator extends PartitionGenerator {
    private final double[][][] distributionTable;

    public CombinationPartitionGenerator(int numberCount, int min, int max, int sum) {
        super(numberCount, min, max, sum, true);
        distributionTable = calculateSolutionCountTable();
    }

    private double[][][] calculateSolutionCountTable() {
        double[][][] table = new double[numberCount + 1][range + 1][sum + 1];
        BigInteger[][] a = new BigInteger[range + 1][sum + 1];
        BigInteger[][] b = new BigInteger[range + 1][sum + 1];
        double[][] t = table[0];
        for (int m = 0; m <= range; m++) {
            a[m][0] = BigInteger.ONE;
            t[m][0] = 1.0;
            for (int s = 1; s <= sum; s++) {
                a[m][s] = BigInteger.ZERO;
                t[m][s] = 0.0;
            }
        }
        for (int n = 1; n <= numberCount; n++) {
            t = table[n];
            for (int m = 0; m <= range; m++)
                for (int s = 0; s <= sum; s++) {
                    BigInteger z;
                    if (m == 0)
                        z = a[0][s];
                    else {
                        z = b[m - 1][s];
                        if (m <= s)
                            z = z.add(a[m][s - m]);
                    }
                    b[m][s] = z;
                    t[m][s] = z.doubleValue();
                }
            // swap a and b
            BigInteger[][] c = b;
            b = a;
            a = c;
        }
        return table;
    }

    @Override
    public int[] get() {
        int[] p = new int[numberCount];
        int m = range; // current max
        int s = sum; // current sum
        for (int i = numberCount - 1; i >= 0; i--) {
            double t = rand.nextDouble() * distributionTable[i + 1][m][s];
            double[][] tableCut = distributionTable[i];
            if (s < m)
                m = s;
            s -= m;
            while (true) {
                t -= tableCut[m][s];
                // m can be 0 here with t > 0 only due to imprecise subtraction
                if (t <= 0 || m == 0)
                    break;
                m--;
                s++;
            }
            p[i] = min + m;
        }
        assert s == 0;
        return p;
    }

    public static final GeneratorFactory factory = (numberCount, min, max, sum) ->
        new CombinationPartitionGenerator(numberCount, min, max, sum);
}

import java.util.*;

public class SmithTromblePartitionGenerator extends PartitionGenerator {
    public SmithTromblePartitionGenerator(int numberCount, int min, int max, int sum) {
        super(numberCount, min, max, sum, false);
    }

    @Override
    public int[] get() {
        List<Integer> ls = new ArrayList<>(numberCount + 1);
        int[] ret = new int[numberCount];
        int increasedSum = sum + numberCount;
        while (true) {
            ls.add(0);
            while (ls.size() < numberCount) {
                int c = 1 + rand.nextInt(increasedSum - 1);
                if (!ls.contains(c))
                    ls.add(c);
            }
            Collections.sort(ls);
            ls.add(increasedSum);
            boolean good = true;
            for (int i = 0; i < numberCount; i++) {
                int x = ls.get(i + 1) - ls.get(i) - 1;
                if (x > range) {
                    good = false;
                    break;
                }
                ret[i] = x;
            }
            if (good) {
                for (int i = 0; i < numberCount; i++)
                    ret[i] += min;
                return ret;
            }
            ls.clear();
        }
    }

    public static final GeneratorFactory factory = (numberCount, min, max, sum) ->
        new SmithTromblePartitionGenerator(numberCount, min, max, sum);
}

import java.util.Arrays;

// Enumerates all partitions with given parameters
public class SequentialEnumerator extends PartitionGenerator {
    private final int max;
    private final int[] p;
    private boolean finished;

    public SequentialEnumerator(int numberCount, int min, int max, int sum, boolean sorted) {
        super(numberCount, min, max, sum, sorted);
        this.max = max;
        p = new int[numberCount];
        startOver();
    }

    private void startOver() {
        finished = false;
        int unshiftedSum = sum + numberCount * min;
        fillMinimal(0, Math.max(min, unshiftedSum - (numberCount - 1) * max), unshiftedSum);
    }

    private void fillMinimal(int beginIndex, int minValue, int fillSum) {
        int fillRange = max - minValue;
        if (fillRange == 0)
            Arrays.fill(p, beginIndex, numberCount, max);
        else {
            int fillCount = numberCount - beginIndex;
            fillSum -= fillCount * minValue;
            int maxCount = fillSum / fillRange;
            int maxStartIndex = numberCount - maxCount;
            Arrays.fill(p, maxStartIndex, numberCount, max);
            fillSum -= maxCount * fillRange;
            Arrays.fill(p, beginIndex, maxStartIndex, minValue);
            if (fillSum != 0)
                p[maxStartIndex - 1] = minValue + fillSum;
        }
    }

    @Override
    public int[] get() { // returns null when there is no more partition, then starts over
        if (finished) {
            startOver();
            return null;
        }
        int[] pCopy = p.clone();
        if (numberCount > 1) {
            int i = numberCount;
            int s = p[--i];
            while (i > 0) {
                int x = p[--i];
                if (x == max) {
                    s += x;
                    continue;
                }
                x++;
                s--;
                int minRest = sorted ? x : min;
                if (s < minRest * (numberCount - i - 1)) {
                    s += x;
                    continue;
                }
                p[i++]++;
                fillMinimal(i, minRest, s);
                return pCopy;
            }
        }
        finished = true;
        return pCopy;
    }

    public static final GeneratorFactory permutationFactory = (numberCount, min, max, sum) ->
        new SequentialEnumerator(numberCount, min, max, sum, false);
    public static final GeneratorFactory combinationFactory = (numberCount, min, max, sum) ->
        new SequentialEnumerator(numberCount, min, max, sum, true);
}

import java.util.*;
import java.util.function.BiConsumer;
import PartitionGenerator.GeneratorFactory;

public class Test {
    private final int numberCount;
    private final int min;
    private final int max;
    private final int sum;
    private final int repeatCount;
    private final BiConsumer<PartitionGenerator, Test> procedure;

    public Test(int numberCount, int min, int max, int sum, int repeatCount,
            BiConsumer<PartitionGenerator, Test> procedure) {
        this.numberCount = numberCount;
        this.min = min;
        this.max = max;
        this.sum = sum;
        this.repeatCount = repeatCount;
        this.procedure = procedure;
    }

    @Override
    public String toString() {
        return String.format("=== %d numbers from [%d, %d] with sum %d, %d iterations ===",
                numberCount, min, max, sum, repeatCount);
    }

    private static class GeneratedVector {
        final int[] v;

        GeneratedVector(int[] vect) {
            v = vect;
        }

        @Override
        public int hashCode() {
            return Arrays.hashCode(v);
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            return Arrays.equals(v, ((GeneratedVector)obj).v);
        }

        @Override
        public String toString() {
            return Arrays.toString(v);
        }
    }

    private static final Comparator<Map.Entry<GeneratedVector, Integer>> lexicographical = (e1, e2) -> {
        int[] v1 = e1.getKey().v;
        int[] v2 = e2.getKey().v;
        int len = v1.length;
        int d = len - v2.length;
        if (d != 0)
            return d;
        for (int i = 0; i < len; i++) {
            d = v1[i] - v2[i];
            if (d != 0)
                return d;
        }
        return 0;
    };

    private static final Comparator<Map.Entry<GeneratedVector, Integer>> byCount =
            Comparator.<Map.Entry<GeneratedVector, Integer>>comparingInt(Map.Entry::getValue)
            .thenComparing(lexicographical);

    public static int SHOW_MISSING_LIMIT = 10;

    private static void checkMissingPartitions(Map<GeneratedVector, Integer> map, PartitionGenerator reference) {
        int missingCount = 0;
        while (true) {
            int[] v = reference.get();
            if (v == null)
                break;
            GeneratedVector gv = new GeneratedVector(v);
            if (!map.containsKey(gv)) {
                if (missingCount == 0)
                    System.out.println(" Missing:");
                if (++missingCount > SHOW_MISSING_LIMIT) {
                    System.out.println("  . . .");
                    break;
                }
                System.out.println(gv);
            }
        }
    }

    public static final BiConsumer<PartitionGenerator, Test> distributionTest(boolean sortByCount) {
        return (PartitionGenerator gen, Test test) -> {
            System.out.print("\n" + getName(gen) + "\n\n");
            Map<GeneratedVector, Integer> combos = new HashMap<>();
            // There's no point of checking permus for sorted generators
            // because they are the same as combos for them
            Map<GeneratedVector, Integer> permus = gen.isSorted() ? null : new HashMap<>();
            for (int i = 0; i < test.repeatCount; i++) {
                int[] v = gen.get();
                if (v == null && gen instanceof SequentialEnumerator)
                    break;
                if (permus != null) {
                    permus.merge(new GeneratedVector(v), 1, Integer::sum);
                    v = v.clone();
                    Arrays.sort(v);
                }
                combos.merge(new GeneratedVector(v), 1, Integer::sum);
            }
            Set<Map.Entry<GeneratedVector, Integer>> sortedEntries = new TreeSet<>(
                    sortByCount ? byCount : lexicographical);
            System.out.println("Combos" + (gen.isSorted() ? ":" : " (don't have to be uniform):"));
            sortedEntries.addAll(combos.entrySet());
            for (Map.Entry<GeneratedVector, Integer> e : sortedEntries)
                System.out.println(e);
            checkMissingPartitions(combos, test.getGenerator(SequentialEnumerator.combinationFactory));
            if (permus != null) {
                System.out.println("\nPermus:");
                sortedEntries.clear();
                sortedEntries.addAll(permus.entrySet());
                for (Map.Entry<GeneratedVector, Integer> e : sortedEntries)
                    System.out.println(e);
                checkMissingPartitions(permus, test.getGenerator(SequentialEnumerator.permutationFactory));
            }
        };
    }

    public static final BiConsumer<PartitionGenerator, Test> correctnessTest =
        (PartitionGenerator gen, Test test) -> {
        String genName = getName(gen);
        for (int i = 0; i < test.repeatCount; i++) {
            int[] v = gen.get();
            if (v == null && gen instanceof SequentialEnumerator)
                v = gen.get();
            if (v.length != test.numberCount)
                throw new RuntimeException(genName + ": array of wrong length");
            int s = 0;
            if (gen.isSorted()) {
                if (v[0] < test.min || v[v.length - 1] > test.max)
                    throw new RuntimeException(genName + ": generated number is out of range");
                int prev = test.min;
                for (int x : v) {
                    if (x < prev)
                        throw new RuntimeException(genName + ": unsorted array");
                    s += x;
                    prev = x;
                }
            } else
                for (int x : v) {
                    if (x < test.min || x > test.max)
                        throw new RuntimeException(genName + ": generated number is out of range");
                    s += x;
                }
            if (s != test.sum)
                throw new RuntimeException(genName + ": wrong sum");
        }
        System.out.format("%30s :   correctness test passed%n", genName);
    };

    public static final BiConsumer<PartitionGenerator, Test> performanceTest =
        (PartitionGenerator gen, Test test) -> {
        long time = System.nanoTime();
        for (int i = 0; i < test.repeatCount; i++)
            gen.get();
        time = System.nanoTime() - time;
        System.out.format("%30s : %8.3f s %10.0f ns/test%n", getName(gen), time * 1e-9, time * 1.0 / test.repeatCount);
    };

    public PartitionGenerator getGenerator(GeneratorFactory factory) {
        return factory.create(numberCount, min, max, sum);
    }

    public static String getName(PartitionGenerator gen) {
        String name = gen.getClass().getSimpleName();
        if (gen instanceof SequentialEnumerator)
            return (gen.isSorted() ? "Sorted " : "Unsorted ") + name;
        else
            return name;
    }

    public static GeneratorFactory[] factories = { SmithTromblePartitionGenerator.factory,
            PermutationPartitionGenerator.factory, CombinationPartitionGenerator.factory,
            SequentialEnumerator.permutationFactory, SequentialEnumerator.combinationFactory };

    public static void main(String[] args) {
        Test[] tests = {
                            new Test(3, 0, 3, 5, 3_000, distributionTest(false)),
                            new Test(3, 0, 6, 12, 3_000, distributionTest(true)),
                            new Test(50, -10, 20, 70, 2_000, correctnessTest),
                            new Test(7, 3, 10, 42, 1_000_000, performanceTest),
                            new Test(20, 3, 10, 120, 100_000, performanceTest)
                       };
        for (Test t : tests) {
            System.out.println(t);
            for (GeneratorFactory factory : factories) {
                PartitionGenerator candidate = t.getGenerator(factory);
                t.procedure.accept(candidate, t);
            }
            System.out.println();
        }
    }
}

आप Ideone पर यह कोशिश कर सकते हैं ।


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

केवल स्पष्ट करने के लिए। क्या यह एल्गोरिथम नमूना बनाने के लिए सभी संभावित विभाजन / रचनाओं को बनाने पर निर्भर करता है ?
जोसेफ वुड

@ जोसेफवुड नहीं, यह उन सभी की गिनती पर निर्भर करता है। यह जनरेटर इनिशियलाइज़ेशन में केवल एक बार किया जाता है और यह प्रभावी है क्योंकि यह डायनेमिक प्रोग्रामिंग दृष्टिकोण का उपयोग करता है।
जॉन मैकक्लेन

कैसे गतिशील प्रोग्रामिंग एक समान यादृच्छिक यादृच्छिक पर चुना एन पूर्णांकों में 'योग' के विभाजन को चुनने का संबंधित समस्या का समाधान कर सकते हैं प्रतिस्थापन के साथ एक सूची (से उदाहरण ) या प्रतिस्थापन के बिना ( उदाहरण ), या कैसे है कि समस्या अन्यथा हल किया जा सकता?
पीटर ओ।

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

1

इस पृष्ठ पर एक अन्य उत्तर में, जॉन मैकक्लेन के परमुटेशनपार्टनर जेनरेटर से एल्गोरिथ्म दिया गया है। इसमें दो चरण होते हैं, अर्थात् एक सेटअप चरण और एक नमूना चरण, और राशि के साथ n[ min, max] में यादृच्छिक संख्या उत्पन्न करता हैsum , जहां संख्या यादृच्छिक क्रम में सूचीबद्ध होती है।

सेटअप चरण: पहला, एक समाधान तालिका निम्न सूत्रों का उपयोग करके बनाई गई है ( t(y, x)जहां y[0, n] में है और x[0, sum - n * min] में है):

  • t (0, j) = 1 यदि j == 0; 0 अन्यथा
  • t (i, j) = t (i-1, j) + t (i-1, j-1) + ... + t (i-1, j- (max-min))

यहां, t (y, x) सापेक्ष संभावना को संग्रहीत करता है कि yसंख्याओं का योग (उपयुक्त सीमा में) बराबर होगा x। यह प्रायिकता सभी t (y, x) के सापेक्ष समान हैy

नमूनाकरण चरण: यहां हम nसंख्याओं का एक नमूना उत्पन्न करते हैं । प्रत्येक स्थिति के लिए सेट sकरें sum - n * min, और 0 से पीछे की ओर काम करना iशुरू करें n - 1:

  • सेट v[0, t (i + 1, s)) में एक यादृच्छिक पूर्णांक ।
  • को सेट rकरेंmin
  • से घटाएँ t (i, s) v
  • जबकि v0 या अधिक रहता है, से t (i, s-1) घटाएँ, v1 जोड़ें r, और 1 से घटाएँs
  • iनमूने में स्थिति की संख्या निर्धारित है r

संपादित करें:

ऐसा प्रतीत होता है कि उपरोक्त एल्गोरिथ्म में तुच्छ परिवर्तनों के साथ, प्रत्येक यादृच्छिक संख्या का उपयोग सभी रेंजों के लिए एक ही श्रेणी के बजाय एक अलग श्रेणी का उपयोग करना संभव है:

पदों पर प्रत्येक यादृच्छिक संख्या i∈ [0,n न्यूनतम मान न्यूनतम (i) और अधिकतम मूल्य अधिकतम (i) होता है।

चलो adjsum= sum- Σmin (i)।

सेटअप चरण: पहला, एक समाधान तालिका निम्न सूत्रों का उपयोग करके बनाई गई है ( t(y, x)जहां y[0, n] में है और x[0, adjsum] में है):

  • t (0, j) = 1 यदि j == 0; 0 अन्यथा
  • t (i, j) = t (i-1, j) + t (i-1, j-1) + ... + t (i-1, j- (अधिकतम (i-1)) -मिन (i) -1)) )

नमूनाकरण चरण तब पहले जैसा ही है, सिवाय इसके कि हम सेट sकरें adjsum(बजाय sum - n * min) और rमिनट के लिए सेट करें (i) (बजाय min)।


संपादित करें:

जॉन मैक्लेन के कॉम्बिनेशनपार्टनर जेनरेटर के लिए, सेटअप और सैंपलिंग चरण निम्नानुसार हैं।

सेटअप चरण: पहला, एक समाधान तालिका निम्न सूत्रों का उपयोग करके बनाई गई है ( t(z, y, x)जहां z[0, n] में yहै, [0 max - min] में है, और x[0, sum - n * min] में है):

  • t (0, j, k) = 1 यदि k == 0; 0 अन्यथा
  • t (i, 0, k) = t (i - 1, 0, k)
  • t (i, j, k) = t (i, j-1, k) + t (i - 1, j, k - P)

नमूनाकरण चरण: यहां हम nसंख्याओं का एक नमूना उत्पन्न करते हैं । सेट sकरने के लिए sum - n * minऔर mrangeकरने के लिए max - minप्रत्येक स्थिति के लिए है, तो i, के साथ शुरू n - 1और 0 करने के लिए पीछे की ओर काम कर रहे:

  • v[0, t (i + 1, mrange, s)) में एक यादृच्छिक पूर्णांक पर सेट करें ।
  • सेट mrangeमिनट के लिए ( mrange, s)
  • घटाएँ mrangeसे s
  • को सेट rकरेंmin + mrange
  • घटाना टी ( i, mrange, s) से v
  • जबकि vअवशेष 0 या इससे अधिक, 1 जोड़ने के लिए s, घटाना से 1 rसे और 1 mrange, तो घटाना टी ( i, mrange, s) सेv
  • iनमूने में स्थिति की संख्या निर्धारित है r

0

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

यदि माध्य एक पूर्णांक है, तो आपका प्रारंभिक सरणी [4, 4, 4, ... 4] या हो सकता है [3, 4, 5, 3, 4, 5, ... 5, 8, 0] या ऐसा कुछ सरल है। 4.5 के मतलब के लिए, [4, 5, 4, 5, ... 4, 5] का प्रयास करें।

अगला संख्याओं की एक जोड़ी चुनें, num1और num2, सरणी में। संभवतः पहले नंबर को क्रम में लिया जाना चाहिए, जैसा कि फिशर-येट्स के फेरबदल के साथ, दूसरे नंबर को यादृच्छिक पर चुना जाना चाहिए। क्रम में पहला नंबर लेना यह सुनिश्चित करता है कि हर नंबर कम से कम एक बार उठाया जाए।

अब गणना max-num1और num2-min। उन दो नंबरों से दूरी maxऔर minसीमाएं हैं। limitदो दूरी के छोटे पर सेट करें । वह अधिकतम परिवर्तन है जो अनुमत सीमाओं के बाहर संख्याओं में से एक या अन्य नहीं डालेगा। अगरlimit शून्य है तो इस जोड़ी को छोड़ें।

[१ limit]] श्रेणी में एक यादृच्छिक पूर्णांक चुनें : इसे कॉल करेंchange । मैं लाइकेबल रेंज से 0 छोड़ देता हूं क्योंकि इसका कोई प्रभाव नहीं है। परीक्षण यह दिखा सकता है कि आप इसे शामिल करके बेहतर यादृच्छिकता प्राप्त करते हैं; मुझे यकीन नहीं है।

अब सेट num1 <- num1 + changeऔर num2 <- num2 - change। यह औसत मूल्य को प्रभावित नहीं करेगा और सरणी के सभी तत्व अभी भी आवश्यक सीमाओं के भीतर हैं।

आपको कम से कम एक बार पूरे सरणी से चलने की आवश्यकता होगी। परीक्षण दिखाना चाहिए कि क्या आपको पर्याप्त रूप से यादृच्छिक रूप से प्राप्त करने के लिए इसे एक से अधिक बार चलाने की आवश्यकता है।

ईटीए: स्यूडोकोड शामिल करें

// Set up the array.
resultAry <- new array size N
for (i <- 0 to N-1)
  // More complex initial setup schemes are possible here.
  resultAry[i] <- mean
rof

// Munge the array entries.
for (ix1 <- 0 to N-1)  // ix1 steps through the array in order.

  // Pick second entry different from first.
  repeat
    ix2 <- random(0, N-1)
  until (ix2 != ix1)

  // Calculate size of allowed change.
  hiLimit <- max - resultAry[ix1]
  loLimit <- resultAry[ix2] - min
  limit <- minimum(hiLimit, loLimit)
  if (limit == 0)
    // No change possible so skip.
    continue loop with next ix1
  fi

  // Change the two entries keeping same mean.
  change <- random(1, limit)  // Or (0, limit) possibly.
  resultAry[ix1] <- resultAry[ix1] + change
  resultAry[ix2] <- resultAry[ix2] - change

rof

// Check array has been sufficiently munged.
if (resultAry not random enough)
  munge the array again
fi

मैंने इसका परीक्षण किया है और दुर्भाग्य से, आपका एल्गोरिथ्म सभी समाधानों का एक समान वितरण नहीं बनाता है, चाहे मैं कितनी भी पुनरावृत्तियों करूँ।
पीटर ओ।

ओह अच्छा। यह वैसे भी कोशिश करने लायक था। :(
रॉसकम

0

जैसा कि ओपी बताते हैं, कुशलतापूर्वक अनारकली की क्षमता बहुत शक्तिशाली है। यदि हम ऐसा करने में सक्षम हैं, तो विभाजन का एक समान वितरण दो चरणों में किया जा सकता है:

  1. पूर्णांकों का एक समान वितरण उत्पन्न करें [1, M], जहां Mविभाजन की कुल संख्या है।
  2. प्रत्येक पूर्णांक को हटा दें

नीचे, हम केवल n वें विभाजन को उत्पन्न करने पर ध्यान केंद्रित करते हैं क्योंकि किसी श्रेणी में पूर्णांक का एक समान वितरण उत्पन्न करने के बारे में जानकारी प्रचुर मात्रा में है। यहां एक सरल C++एल्गोरिथ्म है जिसे अन्य भाषाओं में अनुवाद करना आसान होना चाहिए।

std::vector<int> unRank(int n, int m, int myMax, int nth) {

    std::vector<int> z(m, 0);
    int count = 0;
    int j = 0;

    for (int i = 0; i < z.size(); ++i) {
        int temp = pCount(n - 1, m - 1, myMax);

        for (int r = n - m, k = myMax - 1;
             (count + temp) < nth && r > 0 && k; r -= m, --k) {

            count += temp;
            n = r;
            myMax = k;
            ++j;
            temp = pCount(n - 1, m - 1, myMax);
        }

        --m;
        --n;
        z[i] = j;
    }

    return z;
}

वर्कहोर्स pCountफ़ंक्शन द्वारा दिया जाता है:

int pCount(int n, int m, int myMax) {

    if (myMax * m < n) return 0;
    if (myMax * m == n) return 1;

    if (m < 2) return m;
    if (n < m) return 0;
    if (n <= m + 1) return 1;

    int niter = n / m;
    int count = 0;

    for (; niter--; n -= m, --myMax) {
        count += pCount(n - 1, m - 1, myMax);
    }

    return count;
}

यह फ़ंक्शन उत्कृष्ट उत्तर के आधार पर है क्या पूर्णांक विभाजन के लिए एक कुशल एल्गोरिथ्म है जो सीमित संख्या में भागों के साथ है? उपयोगकर्ता @ m69_snarky_and_unwelcoming द्वारा। ऊपर दिया गया एक सरल एल्गोरिथ्म का एक मामूली संशोधन है (एक ज्ञापन के बिना)। अधिक दक्षता के लिए संस्मरण को शामिल करने के लिए इसे आसानी से संशोधित किया जा सकता है।

यहां ओपी द्वारा दिए गए उदाहरण के साथ एक विचारधारा डेमो है। हम उत्पन्न 100 वें lexicographical विभाजन जहां min = 3, max = 10, n = 7, और sum = 42

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