Ferramenta de pesquisa binária ("bissect")

8

Implemente o algoritmo de pesquisa binária conforme usado para identificar a revisão do código fonte que interrompe um programa de software de computador. Sua ferramenta deve usar dois argumentos, especificando a revisão numerada mais antiga e mais recente a ser pesquisada (dois inteiros positivos), e você tem duas opções para fazer comparações:

  1. Execute o comando shell ./test N, onde N é o número da revisão. Se o teste for aprovado ( ou seja, a revisão for boa), ele retornará o código de saída 0.

  2. Chame a função test(N), que retornará truese o teste for aprovado, falsecaso contrário.

A saída padrão deve ser o número da primeira revisão incorreta e tente tornar o código-fonte da sua ferramenta o mais curto possível. Aproveitar!

PleaseStand
fonte
Apenas alguns esclarecimentos, você quer um script ou apenas uma função?
Nemo157
@ Nemo157: Um script completo. Incluo a test(N)opção de função principalmente para ser justo com as linguagens de programação sem uma maneira padrão de executar comandos do shell, como JavaScript.
PleaseStand

Respostas:

4

Ruby - 92 82 62 60 caracteres

A iterativa é bem mais curta, mas nem de longe tão legal quanto a cauda recursiva.

l,h=$*.map &:to_i
test(m=(l+h)/2)?l=m+1:h=m-1 while l<=h
p l

O método recursivo da cauda antiga para referência

b=proc{|l,h|h<l ?l:(m=(l+h)/2;test(m)?l=m+1:h=m-1;redo)}
puts b[*$*.map(&:to_i)]

Script de teste

Usa um pouco de mágica para injetar a testfunção e executa um arquivo que consiste apenas no código acima.

low = Random.rand(100)
mid = low + Random.rand(1e25)
high = mid + Random.rand(1e50)

File.open('./bisect-test-method.rb','w') do |file|
  file << "def test(value)
             value < #{mid}
           end
          "
end

puts "Using input:"
puts "  low=#{low}"
puts "  high=#{high}"
puts "  first_bad=#{mid}"
puts "Output: #{`ruby -r ./bisect-test-method golf-bisect.rb #{low} #{high}`}"

Resultado:

Using input:
  low=4
  high=75343785543465286986587973836706907796015092187720
  first_bad=5013102893257647460384884
Output: 5013102893257647460384884
Nemo157
fonte
5

Python, 64 caracteres

Este é recursivo, então sobrecarregará a pilha para uma entrada realmente grande

01234567890123456789012345678901234567890123456789012345678901234567890
|         |         |         |         |         |         |         |
 B=lambda L,H:H>L+1 and B(*[L,L/2+H/2,H][test(L/2+H/2):][:2])or H

teste de execução

def test(n):
    print "testing ", n
    return n<66

L,H=10,1000


B=lambda L,H:H>L+1 and B(*[L,L/2+H/2,H][test(L/2+H/2):][:2])or H

print B(L,H)

saídas

testing  505
testing  257
testing  133
testing  71
testing  40
testing  55
testing  62
testing  66
testing  64
testing  65
66
mordedor
fonte
Deve imprimir 66, não 65 (retornar H, não L).
Keith Randall
@Keith, ok mudou isso
gnibbler
2

Python - 77 caracteres

Abusando do módulo python bisect. L é o valor baixo, H é o valor alto

class B:__getitem__=lambda s,n:test(n)
import bisect as b
b.bisect(B(),0,L,H)

aqui está um teste

def test(n):
    print "testing ", n
    return n>66
L,H=10,1000

class B:__getitem__=lambda s,n:test(n)
import bisect as b
b.bisect(B(),0,L,H)

saídas

testing  505
testing  257
testing  133
testing  71
testing  40
testing  56
testing  64
testing  68
testing  66
testing  67

Explicação:

Aqui está como o bisseto é definido. Basicamente, ele espera uma lista e decide se deve dividir para cima ou para baixo com base no valor que encontra olhando a[mid]. Isto chama __getitem__em aque em vez de ser uma lista, é uma classe que eu me definido.

def bisect_right(a, x, lo=0, hi=None):
    """Return the index where to insert item x in list a, assuming a is sorted.

    The return value i is such that all e in a[:i] have e <= x, and all e in
    a[i:] have e > x.  So if x already appears in the list, a.insert(x) will
    insert just after the rightmost x already there.

    Optional args lo (default 0) and hi (default len(a)) bound the
    slice of a to be searched.
    """

    if lo < 0:
        raise ValueError('lo must be non-negative')
    if hi is None:
        hi = len(a)
    while lo < hi:
        mid = (lo+hi)//2
        if x < a[mid]: hi = mid
        else: lo = mid+1
    return lo

bisect=bisect_right
mordedor
fonte
2

Python - 70 caracteres

def B(L,H):
 while L+1<H:M=L+H;L,H=[L,M/2,H][test(M/2):][:2]
 return L

teste de execução

def test(n):
    print "testing ", n
    return n<66
L,H=10,1000

def B(L,H):
 while L+1<H:M=L+H;L,H=[L,M/2,H][test(M/2):][:2]
 return L

print B(L,H)

saídas

testing  505
testing  257
testing  133
testing  71
testing  40
testing  55
testing  63
testing  67
testing  65
testing  66
65
mordedor
fonte