列表中项目的随机分布,具有确切的出现次数

时间:2015-02-16 15:29:09

标签: java list distribution

关注此问题:Random distribution of items in list up to a maximum count

我有一个包含x项的项目列表。

我想在条件中随机分发这些项目:

  
      
  • 每个列表的最大大小为y项(y = 4)
  •   
  • 每个项目必须使用z次(z = 5)
  •   
  • 每个项目只能在特定列表中出现一次
  •   

如果x不能被y和z整除,则可以使列表包含少于y个项目。

我正在寻找这种方法的Java(从1.6到1.8)实现。


到目前为止,感谢Jamie Cockburn,我有一种方法可以使用0 to z次在其他列表中随机分配项目。我需要完全z次使用它们。他的方法还允许在一个列表中多次使用项目。

修改

现在,我设法在列表中使用z次项目。但是如果列表已经包含相同的项目,我会坚持做什么:

这是我的功能:

public static List<List<Item>> distribute(List<Item> list, int y, int z) {
    int x = list.size();
    int nLists = (int) Math.ceil((double)(x*z)/y);

    // Create result lists
    List<List<Item>> result = new ArrayList<>();
    for (int j = 0; j < nLists; j++)
        result.add(new ArrayList<Item>());
    List<List<Item>> outputLists = new ArrayList<>(result);

    // Create item count store
    Map<Item, Integer> itemCounts = new HashMap<>();
    for (Item item : list)
        itemCounts.put(item, 0);

    // Populate results
    Random random = new Random();
    for (int i = 0; i < (x*z); i++) {
        // Add a random item (from the remaining eligible items)
        // to a random list (from the remaining eligible lists)
        Item item = list.get(random.nextInt(list.size()));
        List<Item> outputList = outputLists.get(random.nextInt(outputLists.size()));
        if (outputList.contains(item)) {
            // What do I do here ?
        } else {
            outputList.add(item);

            // Manage eligible output lists
            if (outputList.size() >= y)
                outputLists.remove(outputList);

            // Manage eligible items
            int itemCount = itemCounts.get(item).intValue() + 1;
            if (itemCount >= z)
                list.remove(item);
            else
                itemCounts.put(item, itemCount);
        }
    }

    return result;
}

2 个答案:

答案 0 :(得分:1)

编辑:

一般的想法是,创建输入列表的副本,并从中创建临时列表,同时从密钥列表中删除已挑选的项目。如果清空了copyList,则重新填充。这将强制所有项目在另一个项目被挑选两次之前被选中。如果list.size()可以被y取消,那么结果是真正分配的,如果不是,我不确定。

我将Item更改为Integer进行测试并找出错误。现在它正常运行(我希望,正确满足您的需求)

reineke

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.*;

public class javatest{
public static void main(String args[]){
    List<Integer> test=new ArrayList<Integer>();
    test.add(1);
    test.add(2);
    test.add(3);
    test.add(4);
    test.add(5);
    test.add(6);
    test.add(7);
    System.out.println("Found1");
    List<List<Integer>> temp=distribute(test,3,4);
    for (List<Integer> i:temp){
        System.out.println(i);
    }
}
public static List<List<Integer>> distribute(List<Integer> list, int y, int z) {
    int x = list.size();
    int nLists = (int) Math.ceil((double)(x*z)/y);

    // Create result lists
    List<List<Integer>> result = new ArrayList<>();


    // Create item count store
    Map<Integer, Integer> itemCounts = new HashMap<>();
    for (Integer item : list){
        itemCounts.put(item, 0);
    }
    System.out.println("Found2");
    // Populate results
    Random random = new Random();
    ArrayList<Integer> copyList=new ArrayList<Integer>(); //create a copy of the List of Items. 
    for (int i = 0; i < nLists; i++) {
        System.out.println("Found:"+i);
        System.out.println("listSize: "+list.size());

        //the copyList is reduced for each Item i pick out of it, so i can assure, that no item is used twice in a result, and several item arent picked 5 times and other 0 times, to prevent a error in the end.
        ArrayList<Integer> tempList=new ArrayList<Integer>();
        //if there are less items in the copyList, than fitting in the resultlist, refill it
        if (copyList.size()<y && list.size()>1){
            for (Integer item : list){
                if (!copyList.contains(item)) copyList.add(item);
                else {
                    tempList.add(item);
                    int itemCount = itemCounts.get(item).intValue()+1;
                    if (itemCount >= z) {
                        list.remove(item);
                        copyList.remove(item);
                    }
                    else
                        itemCounts.put(item, itemCount);
                }
            }
        }
        // as long als the tempList isnt filled and there are items in the list to assign, add Items to the tempList
        while (tempList.size()<y && list.size()>0) {
            random=new Random();
            Integer item = copyList.get(random.nextInt(copyList.size()));
            if (!tempList.contains(item)){
                tempList.add(item);
                copyList.remove(item);
                int itemCount = itemCounts.get(item).intValue()+1;
                if (itemCount >= z)
                    list.remove(item);
                else
                    itemCounts.put(item, itemCount);
            }
        }
        result.add(tempList);
    }
    return result;
}
}

答案 1 :(得分:1)

我删除了我的其他答案,因为它完全错了!然后我发现有一个更简单的方法:

public static List<List<Item>> distribute(List<Item> items, int y, int z) {
    // Create list of items * z
    List<Item> allItems = new ArrayList<>();
    for (int i = 0; i < z; i++)
        allItems.addAll(items);
    Collections.shuffle(allItems);

    // Randomly shuffle list
    List<List<Item>> result = new ArrayList<>();
    int totalItems = items.size()*z;
    for (int i = 0; i < totalItems; i += y)
        result.add(new ArrayList<Item>(allItems.subList(i, Math.min(totalItems, i+y))));

    // Swap items in lists until lists are unique
    for (List<Item> resultList : result) {
        // Find duplicates
        List<Item> duplicates = new ArrayList<>(resultList);
        for (Item item : new HashSet<Item>(resultList))
            duplicates.remove(item);

        for (Item duplicate : duplicates) {
             // Swap duplicate for item in another list
            for (List<Item> swapCandidate : result) {
                if (swapCandidate.contains(duplicate))
                    continue;
                List<Item> candidateReplacements = new ArrayList<>(swapCandidate);
                candidateReplacements.removeAll(resultList);
                if (candidateReplacements.size() > 0) {
                    Item replacement = candidateReplacements.get(0);
                    resultList.add(resultList.indexOf(duplicate), replacement);
                    resultList.remove(duplicate);
                    swapCandidate.add(swapCandidate.indexOf(replacement), duplicate);
                    swapCandidate.remove(replacement);
                    break;
                }
            }
        }
    }

    return result;
}

基本上:

  • 创建一个包含items重复z
  • 的大型列表
  • 随机播放列表
  • 将其分成大小为y
  • 的块
  • 使每个列表都是唯一的

与已接受的解决方案相比,这具有以下优势:

  • 不修改原始列表
  • 更快(16.5秒vs 1,000,000次执行的18.8秒)
  • 简单!