Algorithm for random distribution of objects with restrictions

Help me find a good algorithm?

I have a bag full of n balls. (Let's say 28 goals for an example.)

The balls in this bag each have 1 color. There are <= 4 different colors of balls in the bag. (Let them say that red, green, blue and violet are opportunities.)

I have three buckets, each of which has the number of balls they need. These numbers are n. (For example, say, bucket A should end with 7 balls, bucket B should end with 11 balls, bucket C should end with 10 balls.)

Buckets may or may not have color restrictions — colors that they won’t accept. (Bucket A does not accept purple balls or green balls. Bucket B does not accept red balls. C bucket does not accept purple balls or blue balls.)

I need to distribute balls efficiently and randomly (equal probability of all possibilities).

I can’t just accidentally put balls in buckets that have a place to accept them, because this can lead me to a situation where the only bucket in which there is space does not accept the only color that remains in the bag.

It is given that there is always the opportunity to distribute balls at least 1 time. (I will not have a bag of red balls only, and some bucket with a number> 0 will not accept red balls.)

, . ( , C 1, 2, , , , C 2, 1.)

, :

, , . - . , , . , .

Choose a ball from the bag at random. (Call it "this ball".)

If this ball fits and is allowed in a number of buckets > 0:
    Choose one of those buckets at random and put this ball in that bucket.

else (this ball is not allowed in any bucket that it fits in):
    Make a list of colors that can go in buckets that are not full.
    Make a list of balls of those colors that are in full buckets that this ball is allowed in.
    If that 2nd list is length 0 (There are no balls of colors from the 1st list in the bucket that allows the color of this ball):
        ASSERT: (Please show me an example situation where this might not be the case.)
                There is a 3rd bucket that is not involved in the previously used buckets in this algorithm.
                (One bucket is full and is the only one that allows this ball.
                 A second bucket is the only one not full and doesn't allow this ball or any ball in the first bucket.
                 The 3rd bucket is full must allow some color that is in the first bucket and must have some ball that is allowed in the second bucket.)
        Choose, at random, a ball from the 3rd bucket balls of colors that fit in the 2nd bucket, and move that ball to the 2nd bucket.
        Choose, at random, a ball from the 1st bucket balls of colors that fit in the 3rd bucket, and move that ball to the 3rd bucket.
        Put "this ball" (finally) in the 1st bucket.
    else:
        Choose a ball randomly from that list, and move it to a random bucket that is not full.
        Put "this ball" in a bucket that allows it.
Next ball.
+4
4

O (n ^ 3) - . (3 - .)

, , , .

. . ; , . ( , ). ; , . ( , .)

, , , , . (O (n ^ 3) ), . , .

, , , , , .

Python (O (n ^ 4) , ).

#!/usr/bin/env python3
import collections
import random


def make_key(buckets, bucket_sizes):
    return tuple(bucket_sizes[bucket] for bucket in buckets)


def sample(balls, final_bucket_sizes):
    buckets = list(final_bucket_sizes)
    partials = {(0,) * len(buckets): (1, [])}
    for ball in balls:
        next_partials = {}
        for count, partial in partials.values():
            for bucket in ball:
                next_partial = partial + [bucket]
                key = make_key(buckets, collections.Counter(next_partial))
                if key in next_partials:
                    existing_count, existing_partial = next_partials[key]
                    total_count = existing_count + count
                    next_partials[key] = (total_count, existing_partial if random.randrange(total_count) < existing_count else next_partial)
                else:
                    next_partials[key] = (count, next_partial)
        partials = next_partials
    return partials[make_key(buckets, final_bucket_sizes)][1]


def test():
    red = {'A', 'C'}
    green = {'B', 'C'}
    blue = {'A', 'B'}
    purple = {'B'}
    balls = [red] * 8 + [green] * 8 + [blue] * 8 + [purple] * 4
    final_bucket_sizes = {'A': 7, 'B': 11, 'C': 10}
    return sample(balls, final_bucket_sizes)


if __name__ == '__main__':
    print(test())
+1

, , .

, , . , .

, , , NumberOfBucket ^ NumberOfBalls.

, ( B1 N1, ) . :

//let a distribution D be a tuple N1,...,Nx of the current number of balls each bucket can accept

void DistributeColor(Distribution D, Color C) {
  DistributeBucket(D,B1,C);
}

void DistributeBucket(Distribution D, Bucket B, Color C) {
  if B.canAccept(C) {
    for (int i = 0; i<= min(D[N],C.N); i++) {
      //we put i balls of the color C in the bucket B
      C.N-=i;
      D.N-=i;
      if (C.N == 0) {
        //we got no more balls of this color
        if (isLastColor(C)){
          //this was the last color so it is a valid solution
          save(D);
        } else {
          //this was not the last color, try next color
          DistributeColor(D,nextColor(C))
        }
      } else {
        //we still got balls
        if (isNotLastBucket(B)) {
          //This was not the last bucket let try to fill the next one
          DistributeBucket(D, nextBucket(B), C)  
        } else {
          //this was the last bucket, so this distibution is not a solution. Let do nothing (please don't kill yourself :/ )
        }
      }
      //reset the balls for the next try
      C.N+=i;
      D.N+=i;
    }
  }
  //it feel like déjà vu
  if (isNotLastBucket(B)) {
    //This was not the last bucket let try to fill the next one
    DistributeBucket(D, nextBucket(B), C)  
  } else {
    //this was the last bucket, so this distribution is not a solution.
  }
}

( ++ )

0

1 7 28: C28,7 = 1184040 .

2 -, 11 21: C21,11 = 352716.

3 10 C.

, , .

417629852640 ( ).

, . , .

, ( ).

0

, , , , .

-, , . , , .

, , , :

  • . n! . .
  • . , , 7 A, 11 B, 10... C.
  • , . , ; .

, , . , , , . , , .

, . , :

  • A: 7 ; (, )
  • B: 11 ; (, , )
  • C: 10 ; (, )
  • : 6 , 6 , 10 , 6 .

20 . .

, 6 B, . , :

  • : 6 B
  • A: 7 ; (, )
  • B: 5 ; (, )
  • C: 10 ; (, )
  • : 6 , 6 , 10 .

C 10 . 6. : 4 + 6, 5 + 5, 6 + 4. 4 4 C.

  • : 6 B, 4 C, 4 C
  • A: 7 ; (, )
  • B: 5 ; (, )
  • C: 2 ; (, )
  • : 2 , 2 , 10 .

10 . C . B 5 ; 5 A. A 7 ; 3 B. , A 5 , B 3 .

  • : 6 B, 4 C, 4 C, 5 A, 3 B
  • A: 2 ; (, )
  • B: 2 ; (, )
  • C: 2 ; (, )
  • : 2 , 2 , 2 .

: .

80 720 , 1/9. , 28! 7! * 11! * 10! * 80 , .

, , , . :

  • - , ?
  • , ?
  • , ?

, , .

: ? , , , . , , ; .

: , , .

0

Source: https://habr.com/ru/post/1623595/


All Articles