📜  秘书问题(最佳停止问题)

📅  最后修改于: 2021-05-04 22:06:27             🧑  作者: Mango

秘书问题(又称婚姻问题)苏丹的嫁妆问题最佳选择问题是“最佳中止问题”的一个示例。

可以用以下形式表述此问题:想象一个管理员想要从n个可排名的申请人中聘用最好的秘书来担任该职位。申请人按随机顺序逐一接受面试。面试后应立即做出有关每个特定申请人的决定。一旦被拒绝,申请人将无法被召回。在面试过程中,管理员可以将申请人划分为目前为止所有面试的申请人,但不知道尚未见面的申请人的质量。问题是关于最大化选择最佳申请人的可能性的最佳策略(停止规则)。

最佳停止:在数学中,最佳停止或提前停止的理论与选择采取特定动作的时间以最大程度地提高预期收益或最小化预期成本有关。

如果要聘用申请人的决定是在面试所有n名候选人的最后决定,那么一个简单的解决方案是使用最大选择算法来跟踪运行的最大值(以及达到该值的人)并最终选择总体最大值。这个问题的困难之处在于必须在面试候选人后立即做出决定。

1 / e最优策略定律
根据此策略,最佳获胜机率始终至少为1 / e。
最佳停止规则规定始终拒绝接受面试的前n / e个申请人(其中e是自然对数的底数,值为2.71828 ),然后停在比到目前为止接受面试的每个申请人都要好的第一个申请人(或如果从未发生,则继续找最后一个申请人)。该策略被称为1 / e停止规则,因为选择最佳候选者的概率为1 / e ,换言之,此策略选择最佳候选者的时间约为37%

如果您仔细考虑,似乎很可能无法选择第一个候选者,因为第一个候选者没有人可以与之比较。更好的策略是选择一些候选人作为样本,为其余候选人设定基准。因此,样本将被拒绝,并将仅用于设置基准。

  • 如果样本太小,我们将无法获得足够的信息来为其余候选人设定基准。

  • 如果样本太大,尽管我们获得了大量信息,但我们也烧掉了太多潜在候选人。这使我们几乎没有可供选择的候选人,因此使该策略不佳。

  • 最佳策略是选择理想或最佳样本大小(理想样本大小),这可以使用拒绝n / e个候选对象(此n / e是样本大小)的1 / e定律来完成。

    不同n值的最佳样本量和成功概率为
    最佳样本量k = n / e
    成功的概率由下式给出:

    其中x = k / n

    在古典秘书问题中选择最佳申请人的概率收敛至1 / e = 0.368 (大约)

    测试秘书问题的程序:
    注意* :最佳策略并非总能找到最佳候选者,而是在大多数情况下会选择最佳候选者。

    C++
    // C++ Program to test 1/e law for Secretary Problem :
    #include 
    #include 
    #define e 2.71828
    using namespace std;
      
    // To find closest integer of num.
    int roundNo(float num)
    {
        return num < 0 ? num - 0.5 : num + 0.5;
    }
      
    // Finds best candidate using n/e rule. candidate[]
    // represents talents of n candidates.
    void printBestCandidate(int candidate[], int n)
    {
        // Calculating sample size for benchmarking.
        int sample_size = roundNo(n/e);
        cout << "\n\nSample size is " << sample_size << endl;
      
        // Finding best candidate in sample size
        int best = 0; 
        for (int i = 1; i < sample_size; i++)
            if (candidate[i] > candidate[best])
                best = i;
      
        // Finding the first best candidate that is  
        // better than benchmark set.
        for (int i = sample_size; i < n; i++)
            if (candidate[i] >= candidate[best]) {
                best = i;
                break;
            }
      
        if (best >= sample_size)
            cout << endl << "Best candidate found is "
                << best + 1 << " with talent " 
                << candidate[best] << endl;
        else
            cout << "Couldn't find a best candidate\n";
    }
      
    int main()
    {
        int n = 8;
      
        // n = 8 candidates and candidate array contains
        // talents of n candidate where the largest 
        // number means highest talented candidate.
        int candidate[n];
      
        // generating random numbers between 1 to 8 
        // for talent of candidate
        srand(time(0));    
        for (int i = 0; i < n; i++)
            candidate[i] = 1 + rand() % 8;
      
        cout << "Candidate : ";
        for (int i = 0; i < n; i++)
            cout << i + 1 << " ";
        cout << endl;
        cout << "  Talents : ";
        for (int i = 0; i < n; i++)
            cout << candidate[i] << " ";
          
        printBestCandidate(candidate, n);
      
        return 0;
    }


    Java
    // Java Program to test 1/e law for Secretary Problem :
    import java.util.*;
    class GFG
    {
    static double e = 2.71828;
      
    // To find closest integer of num.
    static int roundNo(float num)
    {
        return (int) (num < 0 ? 
                      num - 0.5 : num + 0.5);
    }
      
    // Finds best candidate using n/e rule. candidate[]
    // represents talents of n candidates.
    static void printBestCandidate(int candidate[], int n)
    {
        // Calculating sample size for benchmarking.
        int sample_size = roundNo((float) (n / e));
        System.out.println("\n\nSample size is " + sample_size);
      
        // Finding best candidate in sample size
        int best = 0; 
        for (int i = 1; i < sample_size; i++)
            if (candidate[i] > candidate[best])
                best = i;
      
        // Finding the first best candidate that is 
        // better than benchmark set.
        for (int i = sample_size; i < n; i++)
            if (candidate[i] >= candidate[best])
            {
                best = i;
                break;
            }
      
        if (best >= sample_size)
            System.out.println("\nBest candidate found is " + 
                               (best + 1) + " with talent " + 
                                candidate[best]);
        else
            System.out.print("Couldn't find a best candidate\n");
    }
      
    // Driver Code
    public static void main(String[] args)
    {
        int n = 8;
      
        // n = 8 candidates and candidate array contains
        // talents of n candidate where the largest 
        // number means highest talented candidate.
        int []candidate = new int[n];
      
        // generating random numbers between 1 to 8 
        // for talent of candidate
        Random rand = new Random();
        for (int i = 0; i < n; i++)
            candidate[i] = 1 + rand.nextInt((8 - 1) + 1);
      
        System.out.print("Candidate : ");
        for (int i = 0; i < n; i++)
            System.out.print(i + 1 + " ");
        System.out.println();
        System.out.print("Talents : ");
        for (int i = 0; i < n; i++)
            System.out.print(candidate[i] + " ");
          
        printBestCandidate(candidate, n);
    }
    }
      
    // This code is contributed by 29AjayKumar


    Python3
    # Python3 Program to test 1/e law for
    # Secretary Problem
    import random
    import math
      
    e = 2.71828;
      
    # To find closest integer of num.
    def roundNo(num):
        if(num < 0): 
            return (num - 0.5)
        else: 
            return (num + 0.5);
      
    # Finds best candidate using n/e rule. 
    # candidate[] represents talents of n candidates.
    def printBestCandidate(candidate, n):
          
        # Calculating sample size for benchmarking.
        sample_size = roundNo(n / e);
        print("\n\nSample size is", 
               math.floor(sample_size));
      
        # Finding best candidate in sample size
        best = 0; 
        for i in range(1, int(sample_size)):
            if (candidate[i] > candidate[best]):
                best = i;
      
        # Finding the first best candidate that 
        # is better than benchmark set.
        for i in range(int(sample_size), n):
            if (candidate[i] >= candidate[best]):
                best = i;
                break;
      
        if (best >= int(sample_size)):
            print("\nBest candidate found is", 
                         math.floor(best + 1), 
                  "with talent", math.floor(candidate[best]));
        else:
            print("Couldn't find a best candidate");
      
    # Driver code
    n = 8;
      
    # n = 8 candidates and candidate 
    # array contains talents of n 
    # candidate where the largest 
    # number means highest talented 
    # candidate.
    candidate = [0] * (n);
      
    # generating random numbers between 1 to 8 
    # for talent of candidate
    for i in range(n):
        candidate[i] = 1 + random.randint(1, 8);
    print("Candidate : ", end = "");
      
    for i in range(n):
        print((i + 1), end = " ");
    print("\nTalents : ", end = "");
      
    for i in range(n):
        print(candidate[i], end = " ");
       
    printBestCandidate(candidate, n);
      
    # This code is contributed by mits


    C#
    // C# Program to test 1/e law for Secretary Problem
    using System;
          
    class GFG
    {
    static double e = 2.71828;
      
    // To find closest integer of num.
    static int roundNo(float num)
    {
        return (int) (num < 0 ? 
                      num - 0.5 : num + 0.5);
    }
      
    // Finds best candidate using n/e rule. candidate[]
    // represents talents of n candidates.
    static void printBestCandidate(int []candidate, int n)
    {
        // Calculating sample size for benchmarking.
        int sample_size = roundNo((float) (n / e));
        Console.WriteLine("\n\nSample size is " + 
                                    sample_size);
      
        // Finding best candidate in sample size
        int best = 0; 
        for (int i = 1; i < sample_size; i++)
            if (candidate[i] > candidate[best])
                best = i;
      
        // Finding the first best candidate that is 
        // better than benchmark set.
        for (int i = sample_size; i < n; i++)
            if (candidate[i] >= candidate[best])
            {
                best = i;
                break;
            }
      
        if (best >= sample_size)
            Console.WriteLine("\nBest candidate found is " + 
                              (best + 1) + " with talent " + 
                                           candidate[best]);
        else
            Console.Write("Couldn't find a best candidate\n");
    }
      
    // Driver Code
    public static void Main(String[] args)
    {
        int n = 8;
      
        // n = 8 candidates and candidate array contains
        // talents of n candidate where the largest 
        // number means highest talented candidate.
        int []candidate = new int[n];
      
        // generating random numbers between 1 to 8 
        // for talent of candidate
        Random rand = new Random();
        for (int i = 0; i < n; i++)
            candidate[i] = 1 + rand.Next(1, 8);
      
        Console.Write("Candidate : ");
        for (int i = 0; i < n; i++)
            Console.Write(i + 1 + " ");
        Console.WriteLine();
        Console.Write("Talents : ");
        for (int i = 0; i < n; i++)
            Console.Write(candidate[i] + " ");
          
        printBestCandidate(candidate, n);
    }
    }
      
    // This code is contributed by Princi Singh


    PHP
     
                $candidate[$best])
                $best = $i;
      
        // Finding the first best 
        // candidate that is better
        // than benchmark set.
        for ($i = $sample_size; $i < $n; $i++)
            if ($candidate[$i] >= 
                $candidate[$best])
            {
                $best = $i;
                break;
            }
      
        if ($best >= $sample_size)
            echo "\nBest candidate found is " . 
                             floor($best + 1) . 
                              " with talent " . 
                     floor($candidate[$best]) . "\n";
        else
            echo "Couldn't find a best candidate\n";
    }
      
    // Driver code
    $n = 8;
      
    // n = 8 candidates and candidate 
    // array contains talents of n 
    // candidate where the largest 
    // number means highest talented 
    // candidate.
    $candidate = array_fill(0, $n, 0);
      
    // generating random numbers 
    // between 1 to 8 for talent
    // of candidate
    for ($i = 0; $i < $n; $i++)
        $candidate[$i] = 1 + rand(1, 8);
    echo "Candidate : ";
      
    for ($i = 0; $i < $n; $i++)
        echo ($i + 1) . " ";
    echo "\n Talents : ";
      
    for ($i = 0; $i < $n; $i++)
        echo $candidate[$i] . " ";
      
    printBestCandidate($candidate, $n);
      
    // This code is contributed by mits
    ?>


    输出:

    Candidates : 1 2 3 4 5 6 7 8 
      Talents  : 5 3 8 6 5 7 8 6 
    
    Sample size is 3
    
    Best candidate found is 7 with talent 8
    

    替代最优策略:该策略不是选择样本量为n / e,而是选择n的平方根作为最优样本量(即sqrt(n) )。