Java 随着时间的推移,以概率方式选择一定数量的项目

Java 随着时间的推移,以概率方式选择一定数量的项目,java,math,probability,Java,Math,Probability,我有一个在服务器上运行的企业应用程序,它接受文件。用户每天提交数以万计的文件。客户希望每天自动选择其中50个文件进行审核 这些要求是: 文件进入时必须选择(我们不能等待所有文件进入,然后在一天结束时选择50个) 选择的文件必须符合其他一些标准,他们还没有告诉我,但我确信仍有数千个文件符合这些标准 系统不能是“可玩游戏的”。也就是说,他们不希望提交文件的用户意识到,如果他们等到下午什么的,他们的文件永远不会被审核。这意味着我们不能只选择前50个文件,选择的文件必须在一天中随机分布 我们必须有50个

我有一个在服务器上运行的企业应用程序,它接受文件。用户每天提交数以万计的文件。客户希望每天自动选择其中50个文件进行审核

这些要求是:

  • 文件进入时必须选择(我们不能等待所有文件进入,然后在一天结束时选择50个)
  • 选择的文件必须符合其他一些标准,他们还没有告诉我,但我确信仍有数千个文件符合这些标准
  • 系统不能是“可玩游戏的”。也就是说,他们不希望提交文件的用户意识到,如果他们等到下午什么的,他们的文件永远不会被审核。这意味着我们不能只选择前50个文件,选择的文件必须在一天中随机分布
  • 我们必须有50个文件。或者他们这么说。但我很确定,如果碰巧某一天中午之后,没有用户提交符合条件的文件,而我们只有25个文件,他们会同意的。因此,我可以假设我感兴趣的文件类型在一天中以合理的频率提交
  • 然后我想,我需要一些函数来计算一个文件被选择的概率,它使用当前选择的文件的数量和一天中的时间作为输入

    我已经创建了一个测试线束。请原谅这个不可靠的密码。在这种情况下,“pushTask”线程通过将文件添加到堆栈来模拟传入的文件。此测试中的“文件”只是结尾带有随机数的字符串

    “pullTask”线程模拟从堆栈中拉出的文件。它询问requirementsFunction()该“文件”是否满足所需的额外要求(在本测试中,它只是-它是否以零结尾),并询问probabilityFunction()是否应该选择该文件。如果选择了文件,则会将其打印到System.out

    实际上,我需要一些关于在probabilityFunction()中放置什么的帮助,因为现在里面是垃圾(我把它放在里面了,这样你就可以看到我尝试了什么)。或者如果有人知道使用项目/时间的数学概率函数,那也很好

    package com.playground;
    
    import java.time.Duration;
    import java.time.Instant;
    import java.util.ArrayDeque;
    import java.util.Deque;
    import java.util.Random;
    
    public class ProbabilisticSelection {
    
        private static int TOTAL_FILES = 1000;
        private static int AUDIT_QUANTITY = 10;
        private static int TIME_IN_SECONDS_FOR_ALL_FILES = 10;
        private Random random = new Random();
        private Deque<String> stack = new ArrayDeque<String>();
        private boolean finished;
    
        public static void main(String[] args) throws InterruptedException {
            new ProbabilisticSelection().test();
        }
    
        private void test() throws InterruptedException {
            Instant begin = Instant.now();
    
            Runnable pushTask = () -> {
                while (!finished) {
                    int next = random.nextInt(TOTAL_FILES);
                    String item = "File: " + next;
                    stack.push(item);
                    if (Duration.between(begin, Instant.now()).getSeconds() >= TIME_IN_SECONDS_FOR_ALL_FILES) {
                        finished = true;
                    }
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
    
            Runnable pullTask = () -> {
                int itemNumber = 1;
                while (itemNumber <= AUDIT_QUANTITY && !finished) {
                    String poll = stack.poll();
                    if (requirementsFunction(poll) &&
                            probabilityFunction(itemNumber, Duration.between(begin, Instant.now()))) {
                        System.out.println(itemNumber++ + ": "+ poll);
                    } 
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                finished = true;
                Duration delta = Duration.between(begin, Instant.now());
                System.out.println();
                System.out.println("Retrieved files: " + (itemNumber - 1) + ", should be, " + AUDIT_QUANTITY);
                System.out.println("Time taken: " + delta.getSeconds() + ", should be, " + TIME_IN_SECONDS_FOR_ALL_FILES);
            };
    
            new Thread(pullTask).start();
            new Thread(pushTask).start();
        }
    
        private boolean requirementsFunction(String item) {
            return item != null && item.endsWith("0");
        }
    
        private boolean probabilityFunction(int itemNumber, Duration delta) {
            double limit = ((double)(AUDIT_QUANTITY-itemNumber)/(double)AUDIT_QUANTITY + 1); // probability goes down as number of items goes up
            double tension = (double)TIME_IN_SECONDS_FOR_ALL_FILES/((double)delta.getSeconds() + 1); // probablity goes up as time nears the end
            if (tension == 1) {
                return true;
            }
            double prob = limit * tension * 100; 
            int rand = random.nextInt(1000);
            return prob > rand;
        }
    }
    
    package.com;
    导入java.time.Duration;
    导入java.time.Instant;
    导入java.util.ArrayDeque;
    导入java.util.Deque;
    导入java.util.Random;
    公共类概率统计选举{
    私有静态int-TOTAL_文件=1000;
    私有静态int审计_数量=10;
    所有文件的私有静态整数时间(以秒为单位)=10;
    私有随机=新随机();
    private Deque stack=new ArrayDeque();
    私有布尔完成;
    公共静态void main(字符串[]args)引发InterruptedException{
    新概率选择().test();
    }
    private void test()引发InterruptedException{
    即时开始=即时。现在();
    可运行的pushTask=()->{
    当(!完成){
    int next=random.nextInt(文件总数);
    String item=“文件:”+下一步;
    堆栈。推送(项目);
    if(Duration.between(begin,Instant.now()).getSeconds()>=所有文件的时间(单位:秒){
    完成=正确;
    }
    试一试{
    睡眠(10);
    }捕捉(中断异常e){
    e、 printStackTrace();
    }
    }
    };
    Runnable pullTask=()->{
    int itemNumber=1;
    while(项目编号);
    }
    }
    
    算法被调用,它保证了从一些大型和未知的
    N
    项目中公平地采样
    k
    项目。以下是Java

    我知道沿着这条路线走听起来是一件非常痛苦的事情,但是是否可以使用一个近似函数来根据应用程序在一段时间内记录的实际数据进行提交分布周期?然后将此分布输入选择标准。不幸的是,对我来说,一旦文件被“选择”,我就无法“取消选择”。似乎水库采样填充了“水库”然后以一定的概率替换一个项目,使储液罐总是满的。我不能这样做。@PhilBarr,那么你想要的是不可能的。使用储液罐采样,单个项目的概率是1/N,无论N是什么,即使你事先不知道。如果你不能移除项目,那么根据定义,第一个被选择的项目将具有更高的概率,无论您部署什么算法-它们都是从前K个项目中选择的,K1/N。您只是在这一点上没有信息使选择概率为1/N。水库采样的美丽在于动态的概率调整,对池中的所有项目有效。