分割红包算法

    public void notSplitAdd(int [] arr, List<int[]> prepare, List<int[]> result) {
        if(arr[1] - arr[0] <= 1) {
            result.add(arr);
        } else {
            prepare.add(arr);
        }
    }

    public List<int[]> splitRedPack(int total, int size) {
        if(total <= 0 || size <= 0)  throw new RuntimeException("Segmentation parameter error:" + total + " " + size);
        if(size > total) throw new RuntimeException("Does not support decimal division!");
        List<int[]> result = new ArrayList<>(size);
        if(size == 1) {
            result.add(new int[]{0, total});
            return result;
        }
        if(size == total) {
            for (int i = 0; i < size; i++) {
                result.add(new int[]{i, i+1});
            }
            return result;
        }
        List<int[]> prepare = new ArrayList<>(size);
        ThreadLocalRandom r = ThreadLocalRandom.current();
        for (int i = 0; i < size - 1; i++) {
            int n = r.nextInt(0, i == 0 ? total - 1 : prepare.size());
            if(n >= prepare.size()) {
                notSplitAdd(new int[]{0, n + 1}, prepare, result);
                notSplitAdd(new int[]{n + 1, total}, prepare, result);
            } else {
                int[] arr = prepare.get(n);
                int n2 = r.nextInt(arr[0]+1, arr[1]);
                prepare.remove(n);
                notSplitAdd(new int[]{arr[0], n2}, prepare, result);
                notSplitAdd(new int[]{n2, arr[1]}, prepare, result);
            }
        }
        result.addAll(prepare);
        return result;
    }

    public void check(List<int []> list, int num) {
        if(list.stream().map(a->a[1] - a[0]).reduce(Integer::sum).orElse(0) != num) {
            throw new RuntimeException("求和错误:" + print(list));
        }
    }

    public List<String> print(List<int []> list) {
        return list.stream()
                .sorted(Comparator.comparing(a->a[0]))
                .map(a -> a[0] + "-"+a[1]).toList();
    }

    @Test
    public void test1() {
        System.out.println(print(splitRedPack(10000, 50)));
    }

    @Test
    public void test2() {
        for(int i = 1;i <= 10000;i++) {
            check(splitRedPack(10000, i), 10000);
        }
    }

分割成数组区间的形式,可以通过arr[1] - arr[0]得到想要的金额。该算法在红包分割数在20万左右性能会退化到秒级。

posted @ 2025-06-04 11:11  数学与IT  阅读(14)  评论(0)    收藏  举报