Alat pencarian biner ("membagi dua")

8

Terapkan algoritma pencarian biner seperti yang digunakan untuk mengidentifikasi revisi kode sumber yang merusak program perangkat lunak komputer. Alat Anda harus mengambil dua argumen yang menentukan revisi paling awal dan nomor terbaru untuk pencarian (keduanya bilangan bulat positif), dan Anda memiliki dua opsi untuk membuat perbandingan:

  1. Jalankan perintah shell ./test N, di mana N adalah nomor revisi. Jika tes lolos ( mis . Revisinya bagus), maka akan keluar kode keluar 0.

  2. Panggil fungsi test(N), yang akan kembali truejika tes lulus, falsejika tidak.

Output standar harus berupa angka revisi buruk pertama, dan cobalah membuat kode sumber alat Anda sesingkat mungkin. Nikmati!

PleaseStand
sumber
Hanya beberapa klarifikasi, apakah Anda ingin skrip atau hanya fungsi?
Nemo157
@ Nemo157: Script lengkap. Saya menyertakan test(N)opsi fungsi terutama untuk keadilan bagi bahasa pemrograman tersebut tanpa cara standar untuk menjalankan perintah shell, seperti JavaScript.
PleaseStand

Jawaban:

4

Ruby - 92 82 62 60 karakter

Iteratif jauh lebih pendek, tetapi tidak sedingin ekor rekursif.

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

Metode rekursif ekor lama untuk referensi

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)]

Skrip pengujian

Menggunakan sedikit sihir untuk menyuntikkan testfungsi dan menjalankan file yang murni terdiri dari kode di atas.

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}`}"

Keluaran:

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

Python, 64 karakter

Yang ini bersifat rekursif, sehingga akan meluap tumpukan untuk input yang sangat besar

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

uji coba

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)

output

testing  505
testing  257
testing  133
testing  71
testing  40
testing  55
testing  62
testing  66
testing  64
testing  65
66
gnibbler
sumber
Harus mencetak 66, bukan 65 (pengembalian H, bukan L).
Keith Randall
@Keith, ok ubah itu
gnibbler
2

Python - 77 karakter

Menyalahgunakan modul python bisect. L adalah nilai rendah, H adalah nilai tinggi

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

di sini adalah uji coba

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)

output

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

Penjelasan:

Berikut adalah bagaimana pembagian dua bagian. Pada dasarnya ia mengharapkan daftar dan memutuskan apakah membagi dua naik atau turun berdasarkan nilai yang ditemukannya dengan melihat a[mid]. Panggilan ini __getitem__aktif a, yang alih-alih menjadi daftar, adalah kelas yang telah saya tentukan sendiri.

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
gnibbler
sumber
2

Python - 70 karakter

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

uji coba

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)

output

testing  505
testing  257
testing  133
testing  71
testing  40
testing  55
testing  63
testing  67
testing  65
testing  66
65
gnibbler
sumber