Bagaimana mengelabui heuristik "coba beberapa kasus uji": Algoritma yang tampak benar, tetapi sebenarnya salah


105

Untuk mencoba menguji apakah suatu algoritma untuk beberapa masalah sudah benar, titik awal yang biasa adalah mencoba menjalankan algoritme dengan tangan pada sejumlah kasus uji sederhana - cobalah beberapa contoh contoh masalah, termasuk beberapa "kasus sudut" sederhana. ". Ini adalah heuristik yang hebat: ini adalah cara yang bagus untuk dengan cepat menyingkirkan banyak upaya yang salah pada suatu algoritma, dan untuk mendapatkan pemahaman tentang mengapa algoritma tidak bekerja.

Namun, ketika mempelajari algoritma, beberapa siswa tergoda untuk berhenti di situ: jika algoritme mereka bekerja dengan benar pada beberapa contoh, termasuk semua kotak sudut yang dapat mereka coba pikirkan, maka mereka menyimpulkan bahwa algoritme itu harus benar. Selalu ada siswa yang bertanya: "Mengapa saya perlu membuktikan algoritme saya dengan benar, jika saya bisa mencobanya pada beberapa kasus uji?"

Jadi, bagaimana Anda menipu heuristik "coba beberapa kasus uji"? Saya mencari beberapa contoh bagus untuk menunjukkan bahwa heuristik ini tidak cukup. Dengan kata lain, saya mencari satu atau lebih contoh dari suatu algoritma yang secara dangkal tampak seperti itu mungkin benar, dan yang menghasilkan jawaban yang tepat pada semua input kecil yang mungkin muncul dengan siapa pun, tetapi di mana algoritma tersebut sebenarnya tidak bekerja Mungkin algoritma hanya bekerja dengan benar pada semua input kecil dan hanya gagal untuk input besar, atau hanya gagal untuk input dengan pola yang tidak biasa.

Secara khusus, saya mencari:

  1. Algoritma. Cacat harus berada pada level algoritmik. Saya tidak mencari bug implementasi. (Misalnya, minimal, contohnya harus agnostik bahasa, dan kelemahannya harus lebih terkait dengan masalah algoritme daripada masalah rekayasa perangkat lunak atau implementasi.)

  2. Algoritma yang mungkin masuk akal bagi seseorang. Pseudocode harus terlihat setidaknya masuk akal benar (misalnya, kode yang dikaburkan atau jelas meragukan bukan contoh yang baik). Poin bonus jika ini adalah algoritma yang benar-benar dibuat oleh beberapa siswa ketika mencoba menyelesaikan pekerjaan rumah atau masalah ujian.

  3. Algoritma yang akan melewati strategi uji manual yang masuk akal dengan probabilitas tinggi. Seseorang yang mencoba beberapa kasus uji kecil dengan tangan seharusnya tidak mungkin menemukan kekurangannya. Misalnya, "mensimulasikan QuickCheck dengan tangan pada selusin kasus uji kecil" seharusnya tidak akan mengungkapkan bahwa algoritma tersebut salah.

  4. Lebih disukai, algoritma deterministik. Saya telah melihat banyak siswa berpikir bahwa "coba beberapa kasus uji dengan tangan" adalah cara yang masuk akal untuk memeriksa apakah algoritma deterministik benar, tetapi saya menduga sebagian besar siswa tidak akan menganggap bahwa mencoba beberapa kasus uji adalah cara yang baik untuk memverifikasi probabilistik algoritma. Untuk algoritma probabilistik, seringkali tidak ada cara untuk mengetahui apakah ada output tertentu yang benar; dan Anda tidak dapat menggunakan cukup banyak contoh tangan untuk melakukan tes statistik apa pun yang berguna pada distribusi keluaran. Jadi, saya lebih suka fokus pada algoritma deterministik, karena mereka menjadi lebih bersih ke jantung kesalahpahaman siswa.

Saya ingin mengajarkan pentingnya membuktikan algoritme Anda dengan benar, dan saya berharap menggunakan beberapa contoh seperti ini untuk membantu memotivasi bukti kebenaran. Saya lebih suka contoh yang relatif sederhana dan dapat diakses oleh mahasiswa; contoh yang membutuhkan mesin berat atau satu ton latar belakang matematika / algoritme kurang berguna. Juga, saya tidak ingin algoritma yang "tidak alami"; Meskipun mungkin mudah untuk membuat beberapa algoritma buatan aneh untuk mengelabui heuristik, jika terlihat sangat tidak alami atau memiliki backdoor yang jelas dibangun hanya untuk menipu heuristik ini, mungkin tidak akan meyakinkan siswa. Adakah contoh yang bagus?


2
Saya suka pertanyaan Anda, itu juga terkait dengan pertanyaan yang sangat menarik yang saya lihat di Matematika beberapa hari yang lalu terkait dengan penolakan dugaan dengan konstanta besar. Anda dapat menemukannya di sini
ZeroUltimax

1
Beberapa lebih menggali dan saya menemukan mereka dua algoritma geometrik.
ZeroUltimax

@ ZeroUltimax Anda benar, pt tengah dari 3 pts non-colinear tidak dijamin ada di dalam. Obat cepat adalah dengan mendapatkan pt pada garis antara kiri terjauh dan kanan terjauh. Apakah ada masalah di mana lagi?
InformedA

Premis dari pertanyaan ini tampaknya aneh bagi saya dengan cara saya mengalami kesulitan dalam menggerakkan kepala, tetapi saya pikir yang muncul adalah proses desain algoritma seperti yang dijelaskan pada dasarnya rusak. Bahkan bagi siswa yang tidak 'berhenti sampai di situ' itu adalah malapetaka. 1> tulis algoritma, 2> pikirkan / jalankan test case, 3a> stop atau 3b> buktikan benar. Langkah pertama cukup banyak telah dapat mengidentifikasi kelas masukan untuk masalah domain. Kasus sudut dan algoritma itu sendiri muncul dari mereka. (lanjutan)
Mr.Mindor

1
Bagaimana Anda secara formal membedakan bug implementasi dari algoritma yang cacat? Saya tertarik dengan pertanyaan Anda, tetapi pada saat yang sama saya merasa terganggu oleh kenyataan bahwa situasi yang Anda uraikan tampaknya lebih merupakan aturan daripada pengecualian. Banyak orang yang menguji apa yang mereka implementasikan, tetapi mereka biasanya masih memiliki bug. Contoh kedua dari jawaban yang paling banyak dipilih adalah bug semacam itu.
babou

Jawaban:


70

Kesalahan umum yang saya pikir adalah menggunakan algoritma serakah, yang tidak selalu merupakan pendekatan yang benar, tetapi mungkin berhasil di sebagian besar kasus uji.

Contoh: Denominasi koin, dan angka n , ungkapkan n sebagai jumlah d i : s dengan koin sesedikit mungkin.d1,,dknndi

Pendekatan naif adalah dengan menggunakan koin terbesar yang mungkin pertama, dan dengan rakus menghasilkan jumlah tersebut.

Sebagai contoh, koin dengan nilai , 5 dan 1 akan memberikan jawaban yang benar dengan serakah untuk semua angka antara 1 dan 14 kecuali untuk angka 10 = 6 + 1 + 1 + 1 + 1 + 1 + 1 = 5 + 5 .65111410=6+1+1+1+1=5+5


10
Ini memang contoh yang baik, khususnya yang membuat siswa salah secara rutin. Anda tidak hanya perlu memilih set koin tertentu tetapi juga nilai-nilai tertentu untuk melihat algoritma gagal.
Raphael

2
Selain itu, izinkan saya mengatakan bahwa siswa juga akan sering memiliki bukti yang salah dalam contoh ini (menggunakan beberapa argumen naif yang gagal pada pemeriksaan lebih dekat), sehingga lebih dari satu pelajaran dapat dipelajari di sini.
Raphael

2
Sistem koin Inggris gaya lama (sebelum desimalisasi tahun 1971) memiliki contoh nyata tentang hal ini. Algoritma serakah untuk menghitung empat shilling akan menggunakan setengah mahkota (2½ shilling), koin satu shilling, dan enam pence (½ shilling). Tetapi solusi optimal menggunakan dua florin (masing-masing 2 shilling).
Mark Dominus

1
Memang dalam banyak kasus, algoritma serakah tampak masuk akal, tetapi tidak berfungsi - contoh lain adalah pencocokan bipartit maksimum. Di sisi lain, ada juga contoh di mana sepertinya algoritma serakah seharusnya tidak berfungsi, tetapi ternyata: pohon spanning maksimum.
jkff

62

Saya langsung ingat contoh dari R. Backhouse (ini mungkin ada di salah satu bukunya). Rupanya, ia telah menugaskan tugas pemrograman di mana siswa harus menulis program Pascal untuk menguji kesetaraan dua string. Salah satu program yang dibuat oleh seorang siswa adalah sebagai berikut:

issame := (string1.length = string2.length);

if issame then
  for i := 1 to string1.length do
    issame := string1.char[i] = string2.char[i];

write(issame);

Kami sekarang dapat menguji program dengan input berikut:

"universitas" "universitas" Benar; baik

"course" "course" Benar; baik

"" "" Benar; baik

"universitas" "mata kuliah" Salah; baik

"kuliah" "tentu saja" Salah; baik

"presisi" "ketepatan" Salah, oke

Semua ini tampaknya sangat menjanjikan: mungkin programnya memang bekerja. Tetapi pengujian yang lebih hati-hati dengan mengatakan "murni" dan "benar" mengungkapkan output yang salah. Bahkan, program mengatakan "Benar" jika string memiliki panjang yang sama dan karakter terakhir yang sama!

Namun, pengujian sudah cukup menyeluruh: kami memiliki string dengan panjang berbeda, string dengan panjang sama tetapi konten berbeda, dan bahkan string sama. Selanjutnya, siswa bahkan telah menguji dan mengeksekusi setiap cabang. Anda tidak dapat benar-benar berpendapat bahwa pengujian ceroboh di sini - mengingat program ini memang sangat sederhana, mungkin sulit untuk menemukan motivasi dan energi untuk mengujinya dengan cukup teliti.


Contoh lucu lainnya adalah pencarian biner. Dalam TAOCP, Knuth mengatakan bahwa "meskipun ide dasar pencarian biner relatif mudah, detailnya bisa sangat rumit". Rupanya, bug dalam implementasi pencarian biner Java tidak diketahui selama satu dekade. Itu adalah bug integer overflow, dan hanya dimanifestasikan dengan input yang cukup besar. Rincian rumit dari implementasi pencarian biner juga dibahas oleh Bentley dalam buku Programming Pearls .

Intinya: bisa sangat sulit untuk memastikan algoritma pencarian biner benar dengan hanya mengujinya.


9
Tentu saja, cacatnya cukup jelas dari sumbernya (jika Anda sendiri pernah menulis hal serupa sebelumnya).
Raphael

3
Sekalipun cacat sederhana pada program contoh sudah diperbaiki, string memberikan sedikit masalah menarik! String reversal adalah klasik - cara "dasar" untuk melakukannya adalah dengan hanya membalikkan byte. Kemudian encoding mulai berlaku. Kemudian ganti (biasanya dua kali). Masalahnya, tentu saja, tidak ada cara mudah membuktikan metode Anda secara formal adalah benar.
Biasa

6
Mungkin saya benar-benar salah menafsirkan pertanyaan, tetapi ini tampaknya menjadi cacat dalam implementasi daripada cacat pada algoritma itu sendiri.
Mr.Mindor

8
@ Mr.Mindor: bagaimana Anda dapat mengetahui apakah pemrogram telah menuliskan algoritme yang benar dan kemudian menerapkannya secara salah, atau menuliskan algoritme yang salah dan kemudian menerapkannya dengan setia (Saya ragu untuk mengatakan "dengan benar"!)
Steve Jessop

1
@wabbit Itu masih bisa diperdebatkan. Apa yang jelas bagi Anda mungkin tidak jelas bagi siswa tahun pertama.
Juho

30

Contoh terbaik yang pernah saya temui adalah pengujian primality:

input: bilangan asli p, p! = 2
output: apakah perdana atau tidak?
algoritma: menghitung 2 ** (p-1) mod p. Jika hasil = 1 maka p adalah prima lain p tidak.

Ini berfungsi untuk (hampir) setiap angka, kecuali untuk beberapa contoh pencacah, dan seseorang benar-benar membutuhkan mesin untuk menemukan contoh tandingan dalam periode waktu yang realistis. Contoh tandingan pertama adalah 341, dan densitas sampel tandingan sebenarnya berkurang dengan meningkatnya p, meskipun hampir secara logaritma.

Alih-alih hanya menggunakan 2 sebagai dasar kekuatan, seseorang dapat meningkatkan algoritma dengan juga menggunakan tambahan, meningkatkan bilangan prima kecil sebagai dasar jika prime sebelumnya kembali 1. Dan masih, ada tandingan sampel untuk skema ini, yaitu nomor Carmichael, cukup langka


Tes primitif kulit adalah tes probabilistik, jadi post-kondisi Anda tidak benar.
Femaref

5
ofc itu adalah tes probabilistik tetapi jawabannya dengan baik menunjukkan (lebih umum) bagaimana algoritma probabilistik keliru untuk yang tepat dapat menjadi sumber kesalahan. lebih lanjut tentang nomor Carmichael
vzn

2
Itu contoh yang bagus, dengan batasan: untuk penggunaan praktis pengujian primality yang saya kenal, yaitu pembangkitan kunci kriptografi asimetris, kami menggunakan algoritma probabilistik! Jumlahnya terlalu besar untuk pengujian yang tepat (jika tidak maka mereka tidak akan cocok untuk kripto karena kunci dapat ditemukan oleh kekuatan kasar dalam waktu yang realistis).
Gilles

1
batasan yang Anda maksudkan adalah tes praktis, tidak teoretis, dan utama dalam sistem kripto misalnya RSA mengalami kegagalan langka / sangat tidak mungkin karena alasan-alasan ini, sekali lagi menggarisbawahi pentingnya contoh. yaitu dalam praktiknya terkadang batasan ini diterima sebagai hal yang tidak terhindarkan. ada algoritma waktu P untuk pengujian primality misalnya AKS tetapi mereka terlalu lama untuk nomor "lebih kecil" yang digunakan dalam praktek.
vzn

Jika Anda menguji tidak hanya dengan 2 p, tetapi dengan p untuk 50 nilai acak yang berbeda 2 ≤ a <p, maka sebagian besar orang akan tahu itu probabilistik, tetapi dengan kegagalan sehingga tidak mungkin bahwa lebih besar kemungkinannya kerusakan pada komputer Anda menghasilkan jawaban yang salah. Dengan 2 p, 3 p, 5 p dan 7 p, kegagalan sudah sangat jarang.
gnasher729

21

Inilah salah satu yang dilemparkan kepada saya oleh perwakilan Google di sebuah konvensi yang saya kunjungi. Itu dikodekan dalam C, tetapi bekerja dalam bahasa lain yang menggunakan referensi. Maaf karena harus kode pada [cs.se], tapi itu satu-satunya untuk menggambarkannya.

swap(int& X, int& Y){
    X := X ^ Y
    Y := X ^ Y
    X := X ^ Y
}

Algoritma ini akan bekerja untuk semua nilai yang diberikan kepada x dan y, bahkan jika nilainya sama. Namun itu tidak akan berfungsi jika itu disebut sebagai swap (x, x). Dalam situasi itu, x berakhir sebagai 0. Sekarang, ini mungkin tidak memuaskan Anda, karena Anda entah bagaimana dapat membuktikan operasi ini benar secara matematis, tetapi masih melupakan kasus tepi ini.


1
Trik itu digunakan dalam kontes C curang untuk menghasilkan implementasi RC4 yang cacat . Membaca artikel itu lagi, saya baru menyadari bahwa peretasan ini mungkin dikirimkan oleh @DW
CodesInChaos

7
Cacat ini memang halus - tetapi cacatnya khusus bahasa, jadi itu bukan cacat dalam algoritma; itu cacat dalam implementasi. Orang dapat menemukan contoh-contoh lain dari keanehan bahasa yang membuatnya mudah untuk menyembunyikan kekurangan-kekurangan yang halus, tetapi itu tidak benar-benar apa yang saya cari (saya sedang mencari sesuatu pada tingkat abstraksi algoritma). Bagaimanapun, cacat ini bukan demonstrasi yang ideal nilai bukti; kecuali Anda sudah berpikir tentang aliasing, Anda mungkin akan berakhir dengan masalah yang sama ketika Anda menuliskan "bukti" kebenaran Anda.
DW

Itu sebabnya saya terkejut ini mendapat suara begitu tinggi.
ZeroUltimax

2
@ DW Itu masalah bagaimana model apa yang Anda tetapkan algoritma. Jika Anda turun ke tingkat di mana referensi memori eksplisit (daripada model umum yang mengasumsikan tidak adanya berbagi), ini adalah cacat algoritma. Kekurangannya tidak benar-benar spesifik bahasa, muncul dalam bahasa apa pun yang mendukung berbagi referensi memori.
Gilles

16

Ada seluruh kelas algoritma yang secara inheren sulit untuk diuji: generator angka pseudo-acak . Anda tidak dapat menguji satu output tetapi harus menyelidiki (banyak) seri output dengan alat statistik. Bergantung pada apa dan bagaimana Anda menguji Anda mungkin akan kehilangan karakteristik non-acak.

Salah satu kasus terkenal di mana segalanya berjalan salah adalah RANDU . Itu melewati pengawasan yang tersedia pada saat itu - yang gagal untuk mempertimbangkan perilaku tuple dari output berikutnya. Sudah tiga kali lipat menunjukkan banyak struktur:

Pada dasarnya, tes tidak mencakup semua kasus penggunaan: sementara penggunaan satu dimensi RANDU (mungkin sebagian besar) baik-baik saja, itu tidak mendukung menggunakannya untuk sampel titik tiga dimensi (dengan cara ini).

Sampling pseudo-acak yang tepat adalah bisnis yang rumit. Untungnya, ada ruang uji yang kuat di sana beberapa hari ini, misalnya dieharder yang berspesialisasi dalam membuang semua statistik yang kita ketahui pada generator yang diusulkan. Cukup?

Agar adil, saya tidak tahu apa yang layak Anda buktikan untuk PRNG.


2
contoh yang bagus namun sebenarnya secara umum tidak ada cara untuk membuktikan PRNG tidak memiliki cacat, hanya ada hierarki tak terbatas antara tes yang lebih kuat vs yang lebih kuat. sebenarnya membuktikan bahwa seseorang adalah "acak" dalam arti yang ketat mungkin tidak dapat diputuskan (belum terlihat yang terbukti sekalipun).
vzn

1
Itu ide bagus dari sesuatu yang sulit untuk diuji, tetapi RNG juga sulit untuk dibuktikan. PRNG tidak begitu rentan terhadap bug implementasi karena tidak ditentukan secara spesifik. Tes seperti diehard bagus untuk beberapa kegunaan, tetapi untuk crypto, Anda bisa lulus diehard dan masih bisa ditertawakan keluar ruangan. Tidak ada CSPRNG "terbukti aman", yang terbaik yang dapat Anda harapkan adalah membuktikan bahwa jika CSPRNG Anda rusak maka begitu juga AES.
Gilles

@Gilles Saya tidak mencoba masuk ke crypto, hanya keacakan statistik (saya pikir keduanya memiliki persyaratan orthogonal yang cukup banyak). Haruskah saya menjelaskannya dalam jawaban?
Raphael

1
Keacakan Crypto menyiratkan keacakan statistik. Namun, tidak satu pun memiliki definisi formal matematis, sejauh yang saya tahu, terlepas dari ideal (dan bertentangan dengan konsep PRNG diimplementasikan pada mesin Turing deterministik) gagasan informasi-keacakan teoretis. Apakah keacakan statistik memiliki definisi formal di luar "harus independen dari distribusi yang akan kami uji terhadap"?
Gilles

1
@ vzn: apa artinya menjadi urutan angka acak dapat didefinisikan dalam banyak cara yang mungkin, tetapi yang sederhana adalah "kompleksitas Komolgorov besar". Dalam hal ini, mudah untuk menunjukkan bahwa menentukan keacakan tidak dapat diputuskan.
cody

9

Maksimum lokal 2D

n×nA

(i,j)A[i,j]

A[i,j+1],A[i,j1],A[i1,j],A[i+1,j]A

0134323125014013

maka setiap sel yang dicetak tebal adalah maksimum lokal. Setiap array yang tidak kosong memiliki setidaknya satu maksimum lokal.

O(n2)

AXXA(i,j)X(i,j)(i,j)

AXAX(i,j)A

AA

(i,j)AA(i,j)

n2×n2A(i,j)

T(n)n×nT(n)=T(n/2)+O(n)T(n)=O(n)

Dengan demikian, kami telah membuktikan teorema berikut:

O(n)n×n

Atau sudahkah kita?


T(n)=O(nlogn)T(n)=T(n/2)+O(n)

2
Ini adalah contoh yang indah ! Aku menyukainya. Terima kasih. (Saya akhirnya menemukan kekurangan dalam algoritma ini. Dari cap waktu Anda bisa mendapatkan batas yang lebih rendah pada berapa lama saya. Saya terlalu malu untuk mengungkapkan waktu yang sebenarnya. :-)
DW

1
O(n)

8

Ini adalah contoh-contoh primitif, karena mereka biasa.

(1) Primality in SymPy. Edisi 1789 . Ada pengujian yang salah pada situs web terkenal yang tidak gagal sampai setelah 10 ^ 14. Sementara perbaikannya benar, itu hanya menambal lubang daripada memikirkan kembali masalah.

(2) Primality di Perl 6. Perl6 telah menambahkan is-prime yang menggunakan sejumlah tes MR dengan basis tetap. Ada contoh tandingan yang diketahui, tetapi mereka cukup besar karena jumlah tes standar sangat besar (pada dasarnya menyembunyikan masalah sebenarnya dengan menurunkan kinerja). Ini akan segera diatasi.

(3) Prioritas dalam FLINT. n_isprime () mengembalikan true untuk komposit , sejak diperbaiki. Pada dasarnya masalah yang sama dengan SymPy. Menggunakan database Feitsma / Galway dari pseudoprimes SPRP-2 ke 2 ^ 64 sekarang kita dapat menguji ini.

(4) Matematika Perl :: Primality. is_aks_prime rusak . Urutan ini tampaknya mirip dengan banyak implementasi AKS - banyak kode yang bekerja secara tidak sengaja (mis. Hilang pada langkah 1 dan akhirnya melakukan semuanya dengan pembagian percobaan) atau tidak bekerja untuk contoh yang lebih besar. Sayangnya AKS sangat lambat sehingga sulit untuk diuji.

(5) Pra-2.2 is_prime Pari. Matematika :: tiket Pari . Ini menggunakan 10 pangkalan acak untuk tes MR (dengan seed tetap pada startup, bukan seed tetap GMP setiap panggilan). Ini akan memberi tahu Anda 9 prima sekitar 1 dari setiap panggilan 1M. Jika Anda memilih nomor yang tepat, Anda dapat membuatnya gagal relatif sering, tetapi jumlahnya menjadi lebih jarang, sehingga tidak banyak muncul dalam praktik. Mereka telah mengubah algoritma dan API.

Ini tidak salah, tetapi ini adalah tes probabilistik klasik: Berapa putaran yang Anda berikan, katakanlah, mpz_probab_prime_p? Jika kita berikan 5 putaran, itu pasti terlihat berfungsi dengan baik - angka harus lulus tes Fermat base-210 dan kemudian 5 tes Miller-Rabin yang dipilih sebelumnya. Anda tidak akan menemukan contoh tandingan hingga 3892757297131 (dengan GMP 5.0.1 atau 6.0.0a), jadi Anda harus melakukan banyak pengujian untuk menemukannya. Tetapi ada ribuan contoh tandingan di bawah 2 ^ 64. Jadi, Anda terus meningkatkan jumlahnya. Berapa jauh? Apakah ada musuh? Seberapa penting jawaban yang benar? Apakah Anda membingungkan basis acak dengan basis tetap? Apakah Anda tahu ukuran input apa yang akan Anda berikan?

1016

Ini cukup sulit untuk diuji dengan benar. Strategi saya termasuk unit test yang jelas, ditambah kasus tepi, ditambah contoh kegagalan yang terlihat sebelum atau dalam paket lain, tes vs database yang diketahui jika memungkinkan (misalnya jika Anda melakukan tes MR basis-2 tunggal, maka Anda telah mengurangi kemungkinan yang tidak dapat dihitung secara komputasional tugas pengujian 2 ^ 64 angka untuk menguji sekitar 32 juta angka), dan akhirnya, banyak tes acak menggunakan paket lain sebagai standar. Poin terakhir berfungsi untuk fungsi-fungsi seperti primality di mana ada input yang cukup sederhana dan output yang diketahui, tetapi beberapa tugas seperti ini. Saya telah menggunakan ini untuk menemukan cacat pada kode pengembangan saya sendiri serta masalah sesekali dalam paket perbandingan. Tetapi mengingat ruang input yang tidak terbatas, kami tidak dapat menguji semuanya.

Adapun pembuktian kebenaran, berikut adalah contoh primality lainnya. Metode BLS75 dan ECPP memiliki konsep sertifikat keutamaan. Pada dasarnya setelah mereka melakukan pencarian untuk menemukan nilai yang sesuai dengan bukti mereka, mereka dapat menampilkannya dalam format yang dikenal. Seseorang kemudian dapat menulis verifikasi atau meminta orang lain untuk menulisnya. Ini berjalan sangat cepat dibandingkan dengan pembuatan, dan sekarang salah satu (1) kedua kode tidak benar (maka dari itu mengapa Anda lebih suka programmer lain untuk verifier), atau (2) matematika di balik ide bukti salah. # 2 selalu mungkin, tetapi ini biasanya telah diterbitkan dan ditinjau oleh banyak orang (dan dalam beberapa kasus cukup mudah bagi Anda untuk berjalan sendiri).

Sebagai perbandingan, metode seperti AKS, APR-CL, divisi percobaan, atau tes Rabin deterministik, semua tidak menghasilkan output selain "prima" atau "komposit." Dalam kasus terakhir kita mungkin memiliki faktor yang dapat memverifikasi, tetapi dalam kasus sebelumnya kita tidak memiliki apa pun selain sedikit output ini. Apakah program bekerja dengan benar? Tidak tahu

Penting untuk menguji perangkat lunak pada lebih dari hanya beberapa contoh mainan, dan juga melalui beberapa contoh pada setiap langkah algoritma dan mengatakan "diberi input ini, apakah masuk akal bahwa saya di sini dengan keadaan ini?"


1
Banyak dari ini terlihat seperti (1) kesalahan implementasi (algoritma yang mendasarinya benar tetapi tidak diterapkan dengan benar), yang menarik tetapi bukan inti dari pertanyaan ini, atau (2) pilihan sadar dan sadar untuk memilih sesuatu yang cepat dan sebagian besar berfungsi tetapi mungkin gagal dengan probabilitas yang sangat kecil (untuk kode yang menguji dengan satu basis acak atau beberapa basis tetap / acak, saya berharap bahwa siapa pun yang memilih untuk melakukan yang tahu mereka membuat tradeoff kinerja).
DW

Anda benar pada poin pertama - algoritma + bug yang tepat bukan itu intinya, meskipun diskusi dan contoh-contoh lain mengkonfigurasinya juga. Bidang sudah matang dengan dugaan yang berfungsi untuk jumlah kecil tetapi tidak benar. Untuk poin (2) itu benar untuk beberapa orang, tetapi contoh saya # 1 dan # 3 bukan kasus ini - diyakini bahwa algoritma itu benar (5 basis ini memberikan hasil yang terbukti untuk angka di bawah 10 ^ 16), lalu kemudian menemukan bahwa itu bukan.
DanaJ

Bukankah ini masalah mendasar dengan tes pseudo-primality?
penanggung jawab

asmeurer, ya di # 2 saya dan diskusi selanjutnya dari mereka. Tapi # 1 dan # 3 keduanya menggunakan Miller-Rabin dengan basis yang diketahui untuk memberikan hasil yang benar secara deterministik di bawah ambang batas. Jadi dalam hal ini "algoritma" (menggunakan istilah longgar untuk mencocokkan OP) salah. # 4 bukanlah tes utama yang mungkin, tetapi seperti yang ditunjukkan DW, algoritme berfungsi dengan baik, hanya implementasi yang sulit. Saya memasukkannya karena itu mengarah ke situasi yang serupa: pengujian diperlukan, dan seberapa jauh Anda melampaui contoh sederhana sebelum Anda mengatakan itu bekerja?
DanaJ

Beberapa posting Anda sepertinya cocok dengan pertanyaan sementara beberapa tidak (komentar cf @ DW). Harap hapus contoh (dan konten lainnya) yang tidak menjawab pertanyaan.
Raphael

7

Algoritma Fisher-Yates-Knuth shuffling adalah contoh (praktis) dan salah satu yang dikomentari oleh salah satu penulis situs ini .

Algoritma ini menghasilkan permutasi acak dari array yang diberikan sebagai:

 // To shuffle an array a of n elements (indices 0..n-1):
  for i from n − 1 downto 1 do
       j ← random integer with 0 ≤ j ≤ i
       exchange a[j] and a[i]

ij0ji

Algoritma "naif" dapat berupa:

 // To shuffle an array a of n elements (indices 0..n-1):
  for i from n − 1 downto 1 do
       j ← random integer with 0 ≤ j ≤ n-1
       exchange a[j] and a[i]

Di mana dalam loop elemen yang akan ditukar dipilih dari semua elemen yang tersedia. Namun ini menghasilkan pengambilan sampel yang bias dari permutasi (ada yang terlalu banyak terwakili dll.)

Sebenarnya seseorang dapat membuat shuffling fisher-yates-knuth menggunakan analisis penghitungan sederhana (atau naif) .

nn!=n×n1×n2..nn1

Masalah utama dengan memverifikasi apakah algoritma pengocokan benar atau tidak ( bias atau tidak ) adalah bahwa karena statistik, sejumlah besar sampel diperlukan. The Artikel codinghorror saya link di atas menjelaskan hal itu (dan dengan tes yang sebenarnya).


1
Lihat di sini untuk contoh bukti ketepatan untuk algoritma acak.
Raphael

5

Contoh terbaik (baca: hal yang paling membuat saya sakit hati) yang pernah saya lihat berkaitan dengan dugaan collatz. Saya berada dalam kompetisi pemrograman (dengan hadiah 500 dolar di baris pertama) di mana salah satu masalahnya adalah menemukan jumlah langkah minimum yang diperlukan untuk dua angka untuk mencapai nomor yang sama. Solusinya tentu saja adalah secara bergantian langkah masing-masing sampai mereka berdua mencapai sesuatu yang telah terlihat sebelumnya. Kami diberi kisaran angka (saya pikir itu antara 1 dan 1000000) dan diberi tahu bahwa dugaan collatz telah diverifikasi hingga 2 ^ 64 sehingga semua angka yang kami berikan pada akhirnya akan konvergen pada 1. Saya menggunakan 32-bit bilangan bulat untuk melakukan langkah-langkah dengan Namun. Ternyata ada satu angka yang tidak jelas antara 1 dan 1000000 (170 ribu sesuatu) yang akan menyebabkan bilangan bulat 32-bit meluap pada waktunya. Sebenarnya angka-angka ini sangat langka di bawah 2 ^ 31. Kami menguji sistem kami untuk nomor HUGE yang jauh lebih besar dari 1000000 untuk "memastikan" bahwa tidak terjadi overflow. Ternyata jumlah yang jauh lebih kecil yang baru saja kami uji tidak menyebabkan luapan. Karena saya menggunakan "int" bukannya "long", saya hanya mendapat hadiah 300 dolar daripada hadiah $ 500.


5

Masalah Knapsack 0/1 adalah masalah yang hampir semua siswa pikir dapat dipecahkan oleh algoritma serakah. Itu terjadi lebih sering jika Anda sebelumnya menunjukkan beberapa solusi serakah sebagai versi masalah Knapsack di mana algoritma serakah bekerja .

Untuk masalah-masalah tersebut, di kelas , saya harus menunjukkan bukti untuk Knapsack 0/1 ( pemrograman dinamis ) untuk menghilangkan keraguan dan untuk versi masalah serakah juga. Sebenarnya, kedua bukti itu tidak sepele dan para siswa mungkin menganggapnya sangat membantu. Selain itu, ada komentar tentang ini di CLRS 3ed , Bab 16, Halaman 425-427 .

Masalah: pencuri merampok toko dan dapat membawa berat maksimal W ke ranselnya. Ada n item dan item ih berat wi dan bernilai vi dolar. Barang apa yang harus diambil pencuri? untuk memaksimalkan keuntungannya ?

Masalah Knapsack 0/1 : Penyetelannya sama, tetapi barangnya mungkin tidak dapat dipecah menjadi potongan-potongan yang lebih kecil , sehingga pencuri dapat memutuskan untuk mengambil item atau meninggalkannya (pilihan biner), tetapi mungkin tidak mengambil sebagian kecil dari suatu item .

Dan Anda dapat memperoleh dari siswa beberapa gagasan atau algoritme yang mengikuti gagasan yang sama dengan masalah versi serakah, yaitu:

  • Ambil kapasitas total tas, dan letakkan sebanyak mungkin objek bernilai paling tinggi, dan ulangi metode ini hingga Anda tidak dapat memasukkan lebih banyak objek karena tas penuh atau tidak ada objek dengan berat kurang sama untuk diletakkan di dalam tas.
  • Cara salah lainnya adalah berpikir: letakkan barang yang lebih ringan dan letakkan yang berikut ini dengan harga terendah.
  • ...

Apakah ini membantu Anda? sebenarnya, kita tahu masalah koin adalah versi masalah ransel. Tapi, ada lebih banyak contoh di hutan masalah ransel, dengan contoh, bagaimana dengan Knapsack 2D (itu sangat membantu ketika Anda ingin memotong kayu untuk membuat furnitur , saya melihat di lokal dari kota saya), itu sangat umum berpikir bahwa serakah juga bekerja di sini, tetapi tidak.


Serakah sudah tercakup dalam jawaban yang diterima , tetapi masalah Knapsack khususnya cocok untuk mengatur beberapa jebakan.
Raphael

3

Kesalahan umum adalah menerapkan algoritma pengocokan yang salah. Lihat diskusi di wikipedia .

n!nn(n1)n


1
Ini adalah bug yang baik, tetapi bukan ilustrasi yang bagus untuk mengelabui heuristik kasus pengujian, karena pengujian tidak benar-benar berlaku untuk algoritma pengocokan (ini dilakukan secara acak, jadi bagaimana Anda mengujinya? Apa artinya gagal dalam kasus pengujian, dan bagaimana Anda mendeteksi itu dari melihat output?)
DW

Anda mengujinya secara statistik tentu saja. Keacakan seragam jauh dari "apa pun bisa terjadi dalam output". Apakah Anda tidak curiga jika suatu program mengatakan untuk meniru dadu memberi Anda 100 3 berturut-turut?
Per Alexandersson

Sekali lagi, saya berbicara tentang heuristik siswa "coba beberapa kasus uji dengan tangan". Saya telah melihat banyak siswa berpikir bahwa ini adalah cara yang masuk akal untuk memeriksa apakah algoritma deterministik benar, tetapi saya menduga mereka tidak akan menganggap itu cara yang baik untuk menguji apakah algoritma pengocokan benar (karena algoritma pengocokan acak, ada tidak ada cara untuk mengetahui apakah ada output tertentu yang benar; dalam hal apa pun, Anda tidak dapat menggunakan cukup banyak contoh secara manual untuk melakukan tes statistik yang berguna). Jadi saya tidak berharap algoritma pengocokan akan banyak membantu menjernihkan kesalahpahaman umum.
DW

1
@PerAlexandersson: Sekalipun Anda hanya menghasilkan satu shuffle saja tidak bisa benar-benar acak menggunakan MT dengan n> 2080. Sekarang penyimpangan dari yang diharapkan akan sangat kecil, jadi Anda mungkin tidak akan peduli ... tetapi ini berlaku bahkan jika Anda menghasilkan jauh lebih sedikit dari periode (seperti yang ditunjukkan oleh penilai di atas).
Charles

2
Jawaban ini sepertinya sudah usang oleh Nikos M. yang lebih rumit ?
Raphael

2

Ular PEP450 yang memperkenalkan fungsi statistik ke dalam perpustakaan standar mungkin menarik. Sebagai bagian dari pembenaran untuk memiliki fungsi yang menghitung varians dalam pustaka standar python, penulis Steven D'Aprano menulis:

def variance(data):
        # Use the Computational Formula for Variance.
        n = len(data)
        ss = sum(x**2 for x in data) - (sum(data)**2)/n
        return ss/(n-1)

Di atas tampaknya benar dengan tes kasual:

>>> data = [1, 2, 4, 5, 8]
>>> variance(data)
  7.5

Tetapi menambahkan konstanta ke setiap titik data tidak boleh mengubah varians:

>>> data = [x+1e12 for x in data]
>>> variance(data)
  0.0

Dan varians tidak boleh negatif:

>>> variance(data*100)
  -1239429440.1282566

Masalahnya adalah tentang angka dan bagaimana presisi hilang. Jika Anda ingin presisi maksimum maka Anda harus memesan operasi Anda dengan cara tertentu. Implementasi yang naif mengarah ke hasil yang salah karena ketidaktepatan terlalu besar. Itulah salah satu masalah kursus numerik saya di universitas.


1
n1

2
@ Raphael: Meskipun adil, algoritme yang dipilih dikenal sebagai pilihan yang buruk untuk data titik-mengambang.

2
Ini bukan hanya tentang implementasi operasi tentang angka dan bagaimana presisi hilang. Jika Anda ingin presisi maksimum maka Anda harus memesan operasi Anda dengan cara tertentu. Itulah salah satu masalah kursus numerik saya di universitas.
Christian

Selain komentar akurat Raphael, kekurangan dari contoh ini adalah bahwa saya tidak berpikir bukti kebenaran akan membantu menghindari kesalahan ini. Jika Anda tidak mengetahui seluk-beluk aritmatika floating-point, Anda mungkin berpikir Anda telah membuktikan ini dengan benar (dengan membuktikan bahwa rumusnya valid). Jadi ini bukan contoh ideal untuk mengajar siswa mengapa penting untuk membuktikan algoritme mereka dengan benar. Jika siswa melihat contoh ini, kecurigaan saya adalah mereka malah akan menarik pelajaran "hal-hal perhitungan floating point / numerik itu rumit".
DW

1

Meskipun hal ini kemungkinan tidak sesuai dengan yang Anda cari, tentu mudah untuk memahami dan menguji beberapa kasus kecil tanpa pemikiran lain akan mengarah pada algoritma yang salah.

nn2+n+410<dd divides n2+n+41d<n2+n+41

Solusi yang diusulkan :

int f(int n) {
   return 1;
}

n=0,1,2,,39n=40

Ini "mencoba beberapa kasus kecil dan menyimpulkan suatu algoritma dari hasilnya" pendekatan muncul sering (meskipun tidak sehebat ini di sini) dalam kompetisi pemrograman di mana tekanan adalah untuk datang dengan algoritma yang (a) cepat untuk diimplementasikan dan (b) ) memiliki waktu lari cepat.


5
Saya tidak berpikir ini adalah contoh yang sangat baik, karena sedikit orang akan berusaha menemukan pembagi polinomial dengan mengembalikan 1.
Brian S

1
nn3n

Ini bisa relevan, dalam arti bahwa mengembalikan nilai konstan untuk pembagi (atau caclulation lain), dapat menjadi hasil dari pendekatan algoritmik yang salah untuk masalah (misalnya masalah statistik, atau tidak menangani kasus tepi dari algoritma). Namun jawabannya perlu diulang
Nikos M.

@NikosM. Heh. Saya merasa seperti memukuli kuda mati di sini, tetapi paragraf kedua dari pertanyaan itu mengatakan bahwa "jika algoritma mereka bekerja dengan benar pada beberapa contoh, termasuk semua kotak sudut yang dapat mereka coba pikirkan, maka mereka menyimpulkan bahwa algoritma tersebut harus benar. Selalu ada siswa yang bertanya: "Mengapa saya perlu membuktikan algoritme saya benar, jika saya bisa mencobanya pada beberapa kasus uji?" Dalam contoh ini, untuk 40 nilai pertama (jauh lebih dari nilai siswa adalah kemungkinan untuk mencoba), mengembalikan 1 adalah benar, bagi saya tampaknya itulah yang dicari OP
Rick Decker

Ok, ya, tapi ini sebagai ungkapan sepele (mungkin tipikal benar), tetapi tidak dalam semangat pertanyaan. Masih perlu diulang lagi
Nikos M.
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.