解决秘书问题


13

秘书问题被描述为一个如此著名的问题:

  1. 你需要新秘书
  2. 您有N个申请人,一次可以面试一次
  3. 面试后,您可以为每个申请人评分。您的计分系统永远不会给两个申请人相同的分数
  4. 面试申请人后,您必须立即给出“是”或“否”
  5. 您想要分数最高的申请人

解决方案是采访第一个floor(N/e)申请人,然后接受分数高于所有先前申请人的第一个申请人。如果没有一个更高的申请人,则返回最后一个申请人。有趣的是,这给了最高的申请人1/e时间百分比。 e欧拉数。要获取的值e,您可以使用内置的log或将其硬编码为至少5个小数点。

输入:

不大于的唯一非负整数的非空数组2^31-1

输出:

代表所选候选者的整数。要明确的算法是:

  1. floor(N/e)在数组的第一个元素中找到最大元素。
  2. 遍历其余元素,并返回比第1步中找到的最大值高的第一个元素。
  3. 如果所有元素都不高于,则返回最后一个元素。

例如,假设您的数组是[2,7,4,3,9,20],则N = 6floor(N/e) = 2。数组的前2个元素是[2,7]。的最大值[2,7]7。其余元素为[4,3,9,20]。大于第一个元素79,所以我们返回9

测试用例:

[0]         => 0
[100]       => 100
[100, 45]   => 100
[0, 1]      => 0
[45, 100]   => 45
[1, 4, 5]   => 4
[1, 5, 4]   => 5
[5, 4, 1]   => 1
[5, 1, 4]   => 4
[4, 1, 5]   => 5
[56, 7, 37, 73, 90, 59, 65, 61, 29, 16, 47, 77, 60, 8, 1, 76, 36, 68, 34, 17, 23, 26, 12, 82, 52, 88, 45, 89, 94, 81, 3, 24, 43, 55, 38, 33, 15, 92, 79, 87, 14, 75, 41, 98, 31, 58, 53, 72, 39, 30, 2, 0, 49, 99, 28, 50, 80, 91, 83, 27, 64, 71, 93, 95, 11, 21, 6, 66, 51, 85, 48, 62, 22, 74, 69, 63, 86, 57, 97, 32, 84, 4, 18, 46, 20, 42, 25, 35, 9, 10, 19, 40, 54, 67, 70, 5, 44, 13, 78, 96]
=> 98
[10, 68, 52, 48, 81, 39, 85, 54, 3, 21, 31, 59, 28, 64, 42, 90, 79, 12, 63, 41, 58, 57, 13, 43, 74, 76, 94, 51, 99, 67, 49, 14, 6, 96, 18, 17, 32, 73, 56, 7, 16, 60, 61, 26, 86, 72, 20, 62, 4, 83, 15, 55, 70, 29, 23, 35, 77, 98, 92, 22, 38, 5, 50, 82, 1, 84, 93, 97, 65, 37, 45, 71, 25, 11, 19, 75, 78, 44, 46, 2, 53, 36, 0, 47, 88, 24, 80, 66, 87, 40, 69, 27, 9, 8, 91, 89, 34, 33, 95, 30]
=> 30

您的解决方案必须是O(n),其中n数组的长度在哪里。如果您的语言具有可找到数组最大值的内置函数,则可以假定该函数可以使用O(n)(并希望这样做)。

存在标准漏洞,这是,因此请以您喜欢的语言做出最短的答案!


1
什么e应该被使用?
2016年


1
嗯,现在我了解了该算法的工作原理。我认为您的第二段意味着您永远不会在发言之后再采访候选人。
门把手

1
我之所以特别询问,是因为在某些语言中,定义一个精度为5个小数点的变量比实际使用内置函数要短e(例如Python,其中e=2.71828的小于import math;math.E
Mego 2016年

1
注意:“ 1 / e的时间百分比。”确实很糟糕。概率为1 / e,大约是37%的时间
edc65

Answers:


4

果冻,13个字节

L:Øe³ḣȯ-Ṁ<i1ị

绝对是O(n)算法,希望是O(n)实现。在线尝试!

怎么运行的

L:Øe³ḣȯ-Ṁ<i1ị  Main link. Argument: A (list of scores)

L              Get the length of A.
 :Øe           Divide the length by e, flooring the result.
    ³ḣ         Retrieve the that many scores from the beginning of A.
      ȯ-       Logical OR; replace an empty list with -1.
        Ṁ      Compute the maximum of those scores.
         <     Compare each score in A with that maximum.
          i1   Find the first index of 1 (0 if not found).
            ị  Retrieve the element of A at that index (the last one if 0).

3

CJam,20字节

q~___,1me/i<:e>f>1#=

与丹尼斯的建议类似。

q~___                     Read array, duplicate three times
      ,                   Consume one to find the length
       1me/i              Push e then divide and take floor
            <             Take that many elements from the list
             :e>          Find maximum (Thanks to Dennis)
                f>        Label array elements larger than this as 1
                  1#      Find the first one (won't be in set of elements we've looked in)
                    =     Take that element from the final copy of the array. -1 gives us the last element as required

$W=不在线性时间内运行。
丹尼斯

哦,你是对的。有什么更好的方法可以找到CJam中的最大值吗?
西蒙斯(Simmons)

1
:e>(最多减少)
丹尼斯

@丹尼斯谢谢!
Simmons

2

Java,128 118字节

a->{int c=(int)(a.length/Math.E),i=0,m=-1,t=0;for(;i<a.length;i++){t=a[i];if(i<c)m=t>m?t:m;if(t>m)return t;}return t;}

缩进:

static Function<Integer[], Integer> secretary2 = a -> {
    int c = (int) (a.length/Math.E),     // c = floor(N/E)
        i = 0, m = -1, t = 0;            // declare vars early to save bytes
    for (;i<a.length;i++) {              // for each element of input
        t = a[i];                        // cache element to save bytes
        if (i<c)                         // if before c
            m = t>m ? t : m;             // m = max(m, element)
        if (t>m)                         // if element > m
            return t;                    // return: we've found our best
    }                                    // if never found a good element
    return t;                            // return the last element
};


2

JavaScript(ES6)64

(a,l=a.length/Math.E,x)=>(a.every(v=>--l>0?x>v?1:x=v:(z=v)<x),z)

少打高尔夫球

(
 a, 
 l=a.length/Math.E, // limit for stage 1
 x // init at undefined
)=>(
  a.every(v => --l > 0 // checking for >0 no need to floor
          ? x>v?1:x=v // stage 1, find max in x, always return truthy
          : (z=v)<x ) // stage 2, set z to current value and exit early if z>x
  , z // at last z has the last seen value
)

测试

f=(a,l=a.length/Math.E,x)=>(a.every(v=>--l>0?x>v?1:x=v:(z=v)<x),z)

console.log=x=>O.textContent+=x+'\n'

;[ 
 [0], [100], [0,1], [1,2,3],
 [100, 45],
 [45, 100],
 [1, 4, 5],
 [1, 5, 4],
 [5, 4, 1],
 [5, 1, 4],
 [4, 1, 5],   
 [10, 68, 52, 48, 81, 39, 85, 54, 3, 21, 31, 59, 28, 64, 42, 90, 79, 12, 63, 41, 58, 57, 13, 43, 74, 76, 94, 51, 99, 67, 49, 14, 6, 96, 18, 17, 32, 73, 56, 7, 16, 60, 61, 26, 86, 72, 20, 62, 4, 83, 15, 55, 70, 29, 23, 35, 77, 98, 92, 22, 38, 5, 50, 82, 1, 84, 93, 97, 65, 37, 45, 71, 25, 11, 19, 75, 78, 44, 46, 2, 53, 36, 0, 47, 88, 24, 80, 66, 87, 40, 69, 27, 9, 8, 91, 89, 34, 33, 95, 30],
[56, 7, 37, 73, 90, 59, 65, 61, 29, 16, 47, 77, 60, 8, 1, 76, 36, 68, 34, 17, 23, 26, 12, 82, 52, 88, 45, 89, 94, 81, 3, 24, 43, 55, 38, 33, 15, 92, 79, 87, 14, 75, 41, 98, 31, 58, 53, 72, 39, 30, 2, 0, 49, 99, 28, 50, 80, 91, 83, 27, 64, 71, 93, 95, 11, 21, 6, 66, 51, 85, 48, 62, 22, 74, 69, 63, 86, 57, 97, 32, 84, 4, 18, 46, 20, 42, 25, 35, 9, 10, 19, 40, 54, 67, 70, 5, 44, 13, 78, 96]
].forEach(t=>{
  var r=f(t)
  console.log(r+' : '+t)
})
<pre id=O></pre>


1

Ruby,64个字节

->a{m=a[0...c=a.size/Math::E].max
a[c..-1].find{|n|n>m}||a[-1]}

2
@Doorknob它循环遍历第一层(N / e)元素以找到最大值,然后在最坏的情况下遍历列表的其余部分,将每个元素与最大值进行比较。在这两个部分中,每个元素只有一个比较。
afuous

嗯是的 我误读并以为您在每次迭代中都找到了最大值。
门把手

1
实际上,我认为如果您只是a.find在第二步中这样做仍然是O(n),尽管显然效率要低得多。
histocrat

1
您可以使用(0...c)不包含c的范围。
历史学家

@histocrat是的,应该是O(2n),它是O(n)
并非查尔斯

1

PARI / GP,70字节

如果给定一个单例,这可能会对较旧版本的gp造成麻烦,但至少在18487版本起有效。

v->m=vecmax(v[1..t=#v\exp(1)]);for(i=t+1,#v,v[i]>m&&return(v[i]));v[#v]

1

JavaScript(ES6),79个字节

a=>(m=Math.max(...a.splice(0,a.length/Math.E)),a.slice(a.findIndex(x=>x>m))[0])

工作,因为findIndex返回-1失败,但a.slice(-1)[0]如所期望的回报的阵列的最后一个元素。


1

Python 2,87字节

a=input()
t=int(len(a)/2.71828)
m=max(a[:t]+[-1])
for x in a[t:]:
 if x>m:break
print x

用户以列表形式输入数组,并带有方括号和逗号。Python 2的input()命令在这里很方便。

无论我们是否提早终止流程,我们都会聘用最后一位接受采访的人。


1

Perl 6,43位元组

我认为这是O(n)

{@^a.first(*>max @a[^floor @a/e])//@a[*-1]}

1

Python 3.5;110字节:

def Interview(h):k=max(h[0:int(len(h)/2.71828)-1]);n=max(h[int(len(h)/2.71828)-1:len(h)-1]);return max([k, n])

基本上,上面的操作是首先获取一个提供的数组“ h” ,只要它包含5个以上的项(目前为止 ...),就在第一个(数组长度(len(h ))/欧拉数(至小数点后5位))该数组,然后将该值返回为“ k”。此外,“ n”是数组其余部分的最大值。最后,从函数返回的值是同时包含“ k”和“ n”的数组中的最大值。

注意:max()Python 的功能 O(n)复杂度。

以下是上述代码的更具可读性的非代码高尔夫 版本,其中提供了随机的,唯一的10项数组,以确认其有效:

import random, math

def Interview():
    k = max(h[0:int(len(h)/math.e)-1])
    n = max(h[int(len(h)/math.e)-1:len(h)-1])
    return max([k, n])

h = random.sample(range((2*31)-1), 10)

print(Interview(h))

欢迎来到PPCG!您可以用逗号分隔进口商品。另外,您不需要自己生成数组,因此可以删除该部分代码(只需将数组作为函数的参数即可)
Nathan Merrill

@NathanMerrill是的,我当时正在考虑这样做,但是后来我认为您不太喜欢它,但是现在我知道那真的没关系,我将编辑我的答案。另外,感谢您提供有关逗号分隔我的进口商品的提示。我完全忘记了!
R. Kap

其他提示:您有很多不需要的空格(在逗号后,等号之间。也不需要结尾的打印语句。)
Nathan Merrill

@NathanMerrill感谢您的提示!在进行更多代码编写时,我会牢记这些!:)
R. Kap
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.