“Bazı test davalarını dene” buluşunu nasıl kandırabilirim: Doğru görünen fakat aslında yanlış olan algoritmalar


105

Bazı problemler için bir algoritmanın doğru olup olmadığını test etmek için, normal başlangıç ​​noktası algoritmayı bir dizi basit test durumunda elle çalıştırmayı denemek - birkaç basit "köşe durumu" da dahil olmak üzere birkaç örnek problem örneğinde deneyiniz. ". Bu harika bir buluşsal yöntemdir: Bir algoritmada birçok yanlış girişimi hızlı bir şekilde ayıklamak ve algoritmanın neden işe yaramadığını anlamak için harika bir yoldur.

Bununla birlikte, algoritmaları öğrenirken, bazı öğrenciler orada durma eğilimindedir: eğer algoritmaları denemeyi düşünebilecekleri tüm köşe vakaları da dahil olmak üzere birkaç örnek üzerinde doğru çalışıyorsa, algoritmanın doğru olması gerektiği sonucuna varırlar. Her zaman şunu soran bir öğrenci vardır: "Birkaç test vakasında deneyebilirsem neden algoritmamı doğrulamam gerekiyor?"

Öyleyse, "bir avuç test vakasını dene" buluşsalını nasıl kandırıyorsun? Bu buluşsal yöntemin yeterli olmadığını göstermek için bazı güzel örnekler arıyorum. Başka bir deyişle, yüzeysel olarak doğru gibi görünen ve algoritmanın gerçekte nerede olacağı, ancak algoritmanın gerçekte olduğu küçük girişlerin hepsine doğru cevabı veren bir algoritma örneği arıyorum. çalışmıyor Belki de algoritma tüm küçük girişlerde doğru bir şekilde çalışıyor ve sadece büyük girişler için başarısız oluyor veya sadece alışılmadık bir düzende girişler için başarısız oluyor.

Özellikle, ben arıyorum:

  1. Bir algoritma. Kusur algoritmik seviyede olmalıdır. Uygulama hataları aramıyorum. (Örneğin, en azından, örnek dilin agnostik olması ve kusur, yazılım mühendisliği veya uygulama konularından ziyade algoritmik endişelerle ilgili olmalıdır.)

  2. Birisinin makul bir şekilde bulabileceği bir algoritma. Sahte kod en azından makul bir şekilde doğru görünmelidir (ör. Gizlenmiş veya açıkça şüpheli olan kod iyi bir örnek değildir). Bonus, bir öğrencinin bir ev ödevi veya sınav problemini çözmeye çalışırken gerçekten ortaya çıkardığı bir algoritma ise puan.

  3. Yüksek olasılıkla makul bir manuel test stratejisini geçebilecek bir algoritma. Birkaç küçük test vakasını elle deneyen birinin bu hatayı keşfetmesi mümkün olmamalıdır. Örneğin, "QuickCheck'i bir düzine küçük test durumunda el ile simüle edin", algoritmanın yanlış olduğunu ortaya koyması olası değildir.

  4. Tercihen, deterministik bir algoritma. Bir çok öğrencinin “bazı test vakalarını el ile dene” nin, deterministik bir algoritmanın doğru olup olmadığını kontrol etmenin makul bir yolu olduğunu düşündüğümü gördüm, ancak çoğu öğrencinin birkaç test senaryosunu denemenin olasılıkları doğrulamak için iyi bir yol olduğunu varsaymayacağını düşünüyorum. algoritmaları. Olasılıklı algoritmalar için, herhangi bir özel çıkışın doğru olup olmadığını söylemenin bir yolu yoktur; ve çıktı dağılımı üzerinde faydalı bir istatistiksel test yapmak için yeterli örneği elle veremezsiniz. Bu yüzden, öğrenci yanılgılarının kalbine daha net bir biçimde yaklaşırken deterministik algoritmalara odaklanmayı tercih ederim.

Algoritmanızın doğru olduğunu kanıtlamanın önemini öğretmek istiyorum ve doğruluk kanıtlarını motive etmek için buna benzer birkaç örnek kullanmayı umuyorum. Lisans öğrencileri için nispeten basit ve erişilebilir örnekleri tercih ediyorum; ağır makine veya bir ton matematiksel / algoritmik arka plan gerektiren örnekler daha az yararlıdır. Ayrıca, "doğal olmayan" algoritmalar istemiyorum; Sezgisel kandırmak için bazı garip yapay algoritmalar oluşturmak kolay olsa da, eğer oldukça doğal görünmüyorsa veya sadece bu sezgisel kandırmak için yapılmış açık bir arka kapı varsa, muhtemelen öğrencileri ikna etmeyecektir. İyi örnekler var mı?


2
Sorunuzu çok seviyorum, aynı zamanda geçen gün Matematik konusunu , büyük sabitleri olan varsayımları yanlışlamakla ilgili gördüğüm çok ilginç bir soruyla da ilgili . Burada
ZeroUltimax

1
Biraz daha kazıyor ve o iki geometrik algoritmayı buldum .
ZeroUltimax

@ SıfırUltimax Haklısınız, herhangi bir koliner olmayan levhanın orta noktasının pt içinde olması garanti edilmez. En hızlı çözüm, en soldaki ve en sağdaki arasındaki çizgiye bir puan kazandırmaktır. Nerede sorun var mı?
Bilgilendirilmiş

Bu sorunun öncülü kafamı dolaşmakta zorluk çekeceğim bir şekilde bana tuhaf geliyor, ama sanırım anlatıldığı gibi algoritma tasarım süreci temelde kırılmış bir süreç. 'Orada durmayan' öğrenciler için bile mahkumdur. 1> yazma algoritması, 2> düşün / çalışma test senaryoları, 3a> stop veya 3b> doğru olduğunu ispatla. İlk adım oldukça fazla olan sorun etki alanı için giriş sınıfları belirleme edilecek. Köşe durumlarda ve algoritmanın kendisi bunlardan kaynaklanmaktadır. (devam)
Mr.Mindor,

1
Bir uygulama hatasını resmi olarak hatalı bir algoritmadan nasıl ayırt edersiniz? Sorunuza ilgi duydum, ama aynı zamanda tarif ettiğiniz durumun istisnadan daha kural gibi görünmesi beni rahatsız etti. Birçok insan uyguladıklarını test eder, ancak genellikle hala böcekleri vardır. En çok oy alan cevabın ikinci örneği tam da böyle bir hatadır.
babou

Yanıtlar:


70

Bence yaygın bir hata, her zaman doğru yaklaşım olmayan açgözlü algoritmaları kullanmaktır, ancak çoğu test durumunda işe yarayabilir.

Örnek: Para mezhepler ve bir sayı , ifade toplamı olarak : s mümkün olduğunca az paralar ile. n n d id1,,dknndi

Saf bir yaklaşım ilk önce mümkün olan en büyük madeni parayı kullanmak ve iştahla böyle bir miktar üretmek.

Örneğin, , ve değerine sahip sikkeler, sayıları hariç ile arasındaki tüm sayılar için açgözlü cevaplar verecektir .5 1 1 1465111410=6+1+1+1+1=5+5


10
Bu gerçekten iyi bir örnektir, özellikle öğrencilerin rutin olarak yanlış anladıkları bir örnek. Algoritmanın başarısız olduğunu görmek için yalnızca belirli bozuk para kümelerini değil, aynı zamanda belirli değerleri de seçmeniz gerekir.
Raphael

2
Ek olarak, öğrencilerin de bu örnekte sık sık yanlış kanıtlar olacağını söyleyeyim (daha yakın sınavda başarısız olan bazı saf argümanları kullanarak), bu yüzden birden fazla ders burada öğrenilebilir.
Raphael

2
Eski tarz İngiliz madeni para sistemi (1971 ondalık sayısından önce) bunun gerçek bir örneğini oluşturuyordu. Dört şilin sayılması için açgözlü bir algoritma, yarım taç (2½ şilin), bir şilin jetonu ve altı pense (illing şilin) ​​kullanır. Ancak en uygun çözüm iki florin kullanır (her biri 2 şilin).
Mark Dominus,

1
Nitekim, birçok durumda açgözlü algoritmalar makul gözükmektedir, fakat işe yaramıyor - başka bir örnek maksimum iki taraflı eşleşmedir. Öte yandan, açgözlü bir algoritmanın işe yaramaması gerektiği gibi görünen örnekler de var, ama var: maksimum yayılan ağaç.
jkff

62

Hemen R. Backhouse'dan bir örnek hatırladım (bu onun kitaplarından birinde olmuş olabilir). Görünüşe göre, öğrencilerin iki dizenin eşitliğini test etmek için bir Pascal programı yazması gereken bir programlama ödevi vermişti. Bir öğrenci tarafından başlatılan programlardan biri şuydu:

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

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

write(issame);

Şimdi programı aşağıdaki girdilerle test edebiliriz:

"üniversite" "üniversite" Doğru; tamam

"course" "course" Doğru; tamam

"" "" Doğru; tamam

"üniversite" "ders" Yanlış; tamam

"ders", "ders" Yanlış; tamam

"kesinlik" "kesinlik" False, OK

Bütün bunlar çok umut verici görünüyor: belki de program gerçekten işe yarıyor. Ancak "saf" ve "gerçek" olarak yapılan daha dikkatli bir test, hatalı çıktı olduğunu ortaya çıkarır. Aslında, dizeleri aynı uzunlukta ve aynı son karaktere sahipse, program "Doğru" diyor!

Bununla birlikte, testler oldukça kapsamlıydı: farklı uzunluktaki, eşit uzunluktaki, farklı içerikteki ve hatta eşit dizgelere sahipti. Ayrıca, öğrenci her dalını bile test etmiş ve uygulamıştır. Testin burada dikkatsiz olduğunu iddia edemezsiniz - program gerçekten çok basit olduğu için, yeterince test etmek için motivasyon ve enerjiyi bulmak zor olabilir.


Başka bir sevimli örnek ikili aramadır. TAOCP'da Knuth, "ikili arama temel fikri oldukça basit olmasına rağmen, ayrıntıların şaşırtıcı derecede zor olabileceğini" söylüyor. Görünüşe göre, Java ikili arama uygulamasında bir hata on yıl boyunca farkedilmeden gitti. Bir tamsayı taşması hatasıydı ve yalnızca yeterince büyük girdiyle tezahür etti. İkili arama uygulamalarının zor detayları aynı zamanda Bentley tarafından Programming Pearls kitabında ele alınmıştır .

Alt satır: İkili bir arama algoritmasının sadece test ederek doğru olduğundan emin olmak şaşırtıcı derecede zor olabilir.


9
Elbette, kusur kaynağından oldukça belirgindir (daha önce kendinize benzer bir şey yazdıysanız).
Raphael

3
Örnek programdaki basit hata düzeltilse bile, dizgiler oldukça ilginç problemler verir! Dize ters çevirme klasiktir - bunu yapmanın "temel" yolu baytları ters çevirmektir. Sonra kodlama devreye giriyor. Sonra vekiller (genellikle iki kere). Sorun elbette, yönteminizin doğru olduğunu resmen kanıtlamanın kolay bir yolu olmamasıdır.
Ordous

6
Belki de soruyu tamamen yanlış yorumluyorum, ancak bu , algoritmanın kendisindeki bir kusurdan ziyade uygulamadaki bir kusur gibi görünüyor .
Mr.Mindor,

8
@ Mr.Mindor: programcının doğru bir algoritma yazdığını ve daha sonra yanlış bir şekilde yazdığını veya yanlış bir algoritma yazdığını ve ardından doğru bir şekilde uygulayıp uygulamadığını (nasıl "doğru" demekten çekinmiyorum!)
Steve Jessop

1
@wabbit Bu tartışılabilir. Sizin için açık olan, birinci sınıf öğrencisi için açık olmayabilir.
Juho

30

Karşılaştığım en iyi örnek, ilkellik testi.

giriş: doğal sayı p, p! = 2
çıktı: pa asal mı değil mi?
algoritma: hesaplama 2 ** (p-1) mod p. Sonuç = 1 ise, p asaldır, p değildir.

Bu, çok az sayıda karşı örnek hariç her sayı için (neredeyse) çalışır ve bir gerçekte gerçekçi bir zaman diliminde bir karşı örnek bulmak için bir makineye ihtiyaç duyulur. İlk karşı örnek 341'dir ve karşı örneklerin yoğunluğu aslında yaklaşık olarak logaritmik olmasına rağmen artan p ile azalmaktadır.

Gücün temeli olarak sadece 2 kullanmak yerine, önceki üssün 1 döndüğü durumlarda baz olarak ek, artan küçük asallar kullanarak da algoritma geliştirilebilir. Yine de, bu şemaya karşı bir örnek vardır, yani Carmichael sayıları, Yine de oldukça nadir


Fermat primality testi olasılık testidir, bu nedenle post-koşulunuz doğru değildir.
Femaref

5
Bu olasılıksal bir testtir ancak cevap, kesin olanlar için hatalı olan olasılıksal algoritmaların bir hata kaynağı olabileceğini (daha genel olarak) güzel bir şekilde göstermektedir. daha fazla Carmichael numaraları
vzn

2
Bu sınırlı bir örnekle güzel bir örnek: aşina olduğum ilkellik testinin pratik kullanımı için, yani asimetrik kriptografik anahtar üretimi için olasılıksal algoritmalar kullanıyoruz! Sayılar kesin testler için çok büyük (o zamanlar olmasaydı, şifreler için uygun olmazlardı, çünkü anahtarlar gerçekçi zamanda kaba kuvvet tarafından bulunabilirdi).
Gilles

1
bahsettiğiniz sınırlama pratiktir, teorik değildir ve kripto sistemlerinde, örneğin RSA'daki primer testler, tam da bu sebeplerden dolayı, örneğin önemini vurgulayarak, nadir / yüksek oranda olanaksız hatalara maruz kalır . yani pratikte bazen bu sınırlama kaçınılmaz olarak kabul edilir. Primallik testi için örneğin AKS gibi P zaman algoritmaları vardır, ancak pratikte kullanılan "daha küçük" sayılar için çok uzun zaman alırlar.
vzn

Yalnızca 2 p ile değil , 50 farklı rastgele değer 2 ≤ a <p ile test ederseniz , o zaman çoğu kişi bunun olası olduğunu bilecektir, ancak başarısızlıklarla bilgisayarınızda bir arıza oluşması olasılığı düşüktür yanlış cevap. 2 p, 3 p, 5 p ve 7 p ile başarısızlıklar çok nadirdir.
gnasher729

21

İşte ben gittim bir kongre google reps tarafından bana atılan biri. C olarak kodlandı, ancak referans kullanan diğer dillerde çalışıyor. [Cs.se] kodunu girdiğim için üzgünüm, ancak bunu açıklayan tek kişi bu.

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

Bu algoritma, aynı değerde olsalar bile, x ve y'ye verilen değerler için çalışacaktır. Ancak, takas (x, x) olarak adlandırılırsa çalışmaz. Bu durumda, x 0 olarak sona erer. Şimdi, bu sizi tatmin etmeyebilir, çünkü bir şekilde bu işlemin matematiksel olarak doğru olduğunu ispatlayabilirsiniz, ancak yine de bu uç durumu unutabilirsiniz.


1
Bu numara, kusurlu bir RC4 uygulaması üretmek için aşağıda verilen C yarışmasında kullanıldı . Bu yazıyı tekrar okuduğumda, bu hackün muhtemelen @DW
CodesInChaos

7
Bu kusur gerçekten de ince - ama bu hata dile özgü, ancak algoritmadaki gerçek bir kusur değil; uygulamadaki bir kusur. Biri, ince kusurları gizlemeyi kolaylaştıran başka dil tuhaflık örnekleri bulabilirdi, ama aradığım şey bu değildi (algoritmaların çıkarılması düzeyinde bir şey arıyordum). Her durumda, bu kusur ispatın değerini gösteren ideal bir gösteri değildir; Zaten takma adınızı düşünmüyorsanız, "doğruluk kanıtı" nı yazdığınızda aynı konuyu görmezden gelebilirsiniz.
DW

Bu yüzden bu kadar yüksek oy almamın sürpriziydi.
ZeroUltimax

2
@DW Algoritmayı hangi modelde tanımladığınızla ilgili bir sorun var. Bellek referanslarının açık olduğu bir seviyeye inerseniz (paylaşımın yokluğunu varsayan ortak model yerine), bu bir algoritma hatasıdır. Bu kusur aslında dile özgü değil, bellek referanslarının paylaşılmasını destekleyen herhangi bir dilde ortaya çıkıyor.
Gilles

16

Sahte olarak test etmesi zor olan bir algoritma sınıfı var: sözde rasgele sayı üreteçleri . Tek bir çıktıyı test edemezsiniz ancak bir çok çıktı grubunu istatistiklerle araştırmanız gerekir. Neyi ve nasıl test ettiğinize bağlı olarak, rastgele olmayan özellikleri de kaçırabilirsiniz.

İşlerin feci şekilde yanlış gittiği meşhur bir vaka RANDU'dur . O sırada mevcut olan incelemeyi geçti - ki bu sonraki çıktıların tuple davranışını göz önünde bulundurmadı . Zaten üçlüler çok sayıda yapı gösteriyor:

Temel olarak, testler tüm kullanım durumlarını kapsamıyordu: RANDU'nun tek boyutlu kullanımı (muhtemelen çoğunlukla) iyi olsa da, üç boyutlu noktaları örneklemek için kullanmayı (bu şekilde) desteklemiyordu.

Doğru sözde rasgele örnekleme, zor bir iştir. Neyse ki, orada günlerde güçlü test paketleri var, örneğin, önerilen bir jeneratörde bildiğimiz tüm istatistikleri atmakta uzmanlaşmış bir kalıp kesici . Yeterli mi?

Adil olmak gerekirse, PRNG'ler için ne yapılabilir kanıtlar elde edebileceğinizi bilmiyorum.


2
güzel bir örnek ancak aslında genel olarak herhangi bir PRNG'nin kusur olmadığını kanıtlamanın bir yolu yoktur, sadece güçlü ve zayıf testlerin sonsuz bir hiyerarşisi vardır. Aslında bir tanesini kanıtlamak herhangi bir anlamda "rastgele" olduğu tahmin edilemezdir (bunun kanıtlanmış olduğu görülmemiştir).
vzn

1
Bu, test edilmesi zor bir şey için iyi bir fikirdir, ancak RNG'nin kanıtlanması da zordur. PRNG, uygulama hatalarına kötü bir şekilde belirtildiği kadar eğilimli değildir. Diehard gibi testler bazı kullanımlar için iyidir, ancak kripto için diehard'ı geçebilir ve yine de odadan güldünüz. “Kanıtlanmış emniyetli” bir CSPRNG yoktur, umabileceğiniz en iyi şey, CSPRNG'nizin arızalanması durumunda AES olduğunu kanıtlamaktır.
Gilles

@Gilles Kriptoya girmeye çalışmıyordum, sadece istatistiksel rastgelelik (iki kişide çok fazla dikey şart var sanırım). Bunu cevabında açıkça belirtmeli miyim?
Raphael

1
Kripto rastgeleliği, istatistiksel rastgeleliği ifade eder. Her ikisinin de matematiksel olarak resmi bir tanımı yoktur, bildiğim kadarıyla, ideal (ve deterministik bir Turing makinesinde uygulanan bir PRNG kavramı ile çelişki) bilgi-teorik rastgelelik kavramı dışındadır. İstatistiksel rastgeleliğin ötesinde resmi bir tanımı var mıdır? ”Aleyhinde test edeceğimiz dağılımlardan bağımsız olmalı mı?
Gilles

1
@vzn: rastgele bir sayı dizisi olmanın anlamı, birçok olası yolla tanımlanabilir, fakat basit olanı "büyük Komolgorov karmaşıklığı" dır. Bu durumda, rastgele olmanın belirlenmesinin kararsız olduğunu göstermek kolaydır.
cody

9

2B yerel maksimum

n×nA

(i,j)A[i,j]

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

0134323125014013

daha sonra her kalın hücre yerel bir maksimumdur. Boş olmayan her dizinin en az bir yerel maksimumu vardır.

O(n2)

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

AXA( ı , j ) A X(i,j)A

Lemma. Çeyrek yerel maksimum . AAA

Kanıt. Hücrede başlamayı düşünün . Yerel bir maksimum değilse, daha büyük bir değere sahip bir komşuya gidin. Bu, yerel bir maksimum olan bir hücreye ulaşana kadar tekrar edilebilir. Bu son hücrenin olması gerekir , çünkü her tarafa değerleri hücrenin değerinden daha küçük olan hücreler . Bu lemmayı kanıtlar. A A ( i , j ) (i,j)AA(i,j)

Algoritma, kendisini yerel olarak bir maksimum bulmak için alt dizi da tekrarlı olarak çağırır ve daha sonra o hücreyi döndürür.n2×n2A(i,j)

Bir matrisi için çalışma süresi, karşılar , bu nedenle . T(n)n×nT(n)=T(n/2)+O(n)T(n)=O(n)

Böylece, aşağıdaki teoremi ispatladık:

Teorem. Bir vardır , bir yerel-en fazla bulmak için -zaman algoritma dizi.O(n)n×n

Yoksa biz mi?


Hala bunu özlüyorum. Yapana kadar kısa bir soru: Demek istediğin, çalışma süresinin mu demek istedin ? (Bu yinelenmenin çözümü olduğu için .)T ( n ) = T ( n / 2 ) + O ( n )T(n)=O(nlogn)T(n)=T(n/2)+O(n)
DW

2
Bu güzel bir örnek! Onu seviyorum. Teşekkür ederim. (Sonunda bu algoritmanın kusurunu çözdüm. Zaman damgalarından beni ne kadar sürdüğü konusunda daha düşük bir sınırlama elde edebilirsiniz. Gerçek zamanı ortaya çıkarmak için çok utanıyorum. :-)
DW

1
Güzel bir bölüm bu algoritmanın neredeyse doğru olmasıdır: 'cross' yerine bir 'window' seçersek, düzgün şekilde tekrar oluşturabilir ve bir algoritması alabiliriz. Ayrıca bkz . Courses.csail.mit.edu/6.006/spring11/lectures/lec02.pdf . O(n)
Ayrık kertenkele

8

Bunlar ilkel örneklerdir, çünkü bunlar yaygındır.

(1) SymPy'deki asallık. Sayı 1789 . Tanınmış bir web sitesine, 10 ^ 14’ten sonraya kadar başarısız olmayan yanlış bir test yapıldı. Düzeltme doğru olsa da, konuyu yeniden düşünmek yerine sadece yamaları deliyordu.

(2) Perl 6'daki primallik. Perl6, sabit tabanlı bir dizi MR testi kullanan asal bir özellik ekledi. Bilinen karşı örnekler var, ancak varsayılan test sayısı çok büyük olduğu için oldukça büyükler (temel olarak performansı düşürerek asıl sorunu gizliyorlar). Bu yakında ele alınacak.

(3) FLINT’de ilkellik. n_isprime () düzeltildiğinden beri kompozitler için true değerini döndürüyor . Temelde SymPy ile aynı konu. SPRP-2 psödoprimlerinin Feitsma / Galway veritabanını 2 ^ 64'e kullanarak şimdi bunları test edebiliriz.

(4) Perl's Math :: Primality. is_aks_prime bozuk . Bu sekans, birçok AKS uygulamasına benziyor - ya kazayla çalışan (örneğin 1. adımda kaybedilen ve her şeyi deneme bölümünde yaparak bitiren) ya da daha büyük örnekler için işe yaramayan birçok kod. Ne yazık ki AKS o kadar yavaş ki test etmek zor.

(5) Pari'nin 2.2 öncesi isprime. Matematik :: Pari bileti . MR testleri için 10 rasgele baz kullandı (GMP'nin her çağrısında sabit tohum yerine başlangıçta sabit tohum ile). Size 9 her 1M aramadan 1'i hakkında asal olduğunu söyleyecektir. Doğru sayıyı seçerseniz göreceli olarak sık sık başarısız olmasına neden olabilirsiniz, ancak sayılar daha seyrekleşir, bu yüzden pratikte pek görünmez. O zamandan beri algoritmayı ve API'yi değiştirdiler.

Bu yanlış değil ama olasılık testlerinden geçiyor: Kaç tur atıyorsunuz, mpz_probab_prime_p? 5 tur verirsek, iyi sonuç verir gibi görünüyor - sayıların bir temel 210 Fermat testini geçmesi ve ardından önceden seçilmiş 5 temel Miller-Rabin testini geçmesi gerekiyor. 3892757297131 (GMP 5.0.1 veya 6.0.0a ile) kadar bir karşı örnek bulamayacaksınız, bu yüzden bulmak için çok fazla test yapmanız gerekecek. Fakat 2 ^ 64'ün altında binlerce karşı örnek var. Yani sayıyı yükseltiyorsun. Ne kadar uzak? Bir rakip var mı? Doğru cevap ne kadar önemlidir? Rasgele tabanları sabit tabanlarla karıştırıyor musunuz? Hangi giriş boyutlarında verileceğini biliyor musunuz?

İlişkili bir nokta var: Büyük sayı nedir? Öğrencilere göre pek çok kişi 10.000'in çok büyük olduğunu düşünüyor . Birçok programcı için büyük bir sayıdır. Kriptografi üzerine çalışan programcılar için bunlar küçük ve büyük, 4096 bit demek. Hesaplamalı sayı teorisi üzerinde çalışan programcılar için, hepsi küçük ve büyük 10 ila 100 bin ondalık basamak olabilir. Bazı matematikçilere göre, bu örneklerden daha küçük olanlardan daha büyük sayılar olduğundan daha büyük sayılar olduğu düşünülürse, bunların hepsi "büyük değil" olarak kabul edilebilir. Bu, birçok insanın düşünmediği bir şeydir, ancak doğruluk ve performans hakkında düşünürken fark yaratır.1016

Bunları doğru test etmek oldukça zordur. Stratejim, açık birim testler, artı son durumlar, artı daha önce veya başka paketlerde görülen hata örneklerini, mümkün olduğunda bilinen veritabanlarını test etmeyi içerir (örneğin, eğer tek bir baz-2 MR testi yaparsanız, hesaplama açısından olanaksız olanı düşürürsünüz. yaklaşık 32 milyon sayıyı sınamak için 2 ^ 64 sayı sınama görevi) ve son olarak, standart olarak başka bir paket kullanarak çok sayıda rasgele sınama. Son nokta, oldukça basit bir girişin ve bilinen bir çıktının olduğu asallık gibi işlevler için işe yarar, ancak oldukça az sayıda görev bunun gibidir. Bunu hem kendi geliştirme kodumdaki kusurları hem de karşılaştırma paketlerindeki zaman zaman görülen sorunları bulmak için kullandım. Ancak sonsuz girdi alanına bakıldığında, her şeyi test edemiyoruz.

Doğruluk kanıtlamak gelince, burada başka bir primalite örneği. BLS75 yöntemleri ve ECPP, bir primallik sertifikası kavramına sahiptir. Temel olarak ispatlarına uygun değerleri bulmak için arama yapmaktan kaçındıktan sonra, bunları bilinen bir formatta çıkarabilirler. Kişi daha sonra bir doğrulayıcı yazabilir veya başka birinin yazmasını sağlayabilir. Bunlar oluşturma işlemine kıyasla çok hızlı çalışıyor ve şimdi (1) her iki kod parçası da yanlış (dolayısıyla doğrulayıcılar için neden diğer programcıları tercih edersiniz) ya da (2) ispat fikrinin arkasındaki matematik yanlış. # 2 her zaman mümkündür, ancak bunlar genellikle birden fazla kişi tarafından yayınlanmış ve incelenmiştir (ve bazı durumlarda kendi başınıza yürümeniz için yeterince kolaydır).

Buna karşılık, AKS, APR-CL, deneme bölümü veya deterministik Rabin testi gibi yöntemlerin tümü "asal" veya "kompozit" ten başka bir çıktı üretmiyor. İkinci durumda, doğrulayabileceğimiz bir faktör olabilir, ancak önceki durumda bu bitin dışında bir şey kalmadı. Program doğru çalıştı mı? Dunno.

Yazılımı birkaç oyuncak örneğinden daha fazlası üzerinde test etmek ve algoritmanın her aşamasında bazı örneklerden geçmek ve "bu girdiyi verdiğimde, burada olduğumda bir anlam ifade ediyor mu?" Demek önemlidir.


1
Bunların birçoğu (1) uygulama hataları (temel algoritma doğru, ancak doğru bir şekilde uygulanmadı) ya da bu sorunun amacı değil, ya da (2) bir şeyi seçmek için bilinçli ve bilinçli bir seçim gibi görünüyor. hızlıdır ve çoğunlukla çalışır ancak çok küçük bir olasılıkla başarısız olabilir (bir rastgele tabanla ya da birkaç sabit / rastgele tabanla test eden kod için, bunu yapmayı seçenlerin bir performans değişikliği yaptıklarını bildiğini umardım).
DW

Birinci noktada haklısınız - doğru algoritma + hata nokta değil, tartışma ve diğer örnekler de onları birbirine karıştırıyor. Alan, küçük sayılar için işe yarayan ama hatalı olan varsayımlarla olgunlaşmıştır. Bazıları için doğru olan nokta (2) için, ancak örneklerim # 1 ve # 3 bu durum değildi - algoritmanın doğru olduğuna inanılıyordu (bu 5 baz, 10 ^ 16 altındaki sayılar için kanıtlanmış sonuçlar verdi), sonra olmadığını keşfetti.
DanaJ

Bu sözde primallik testleriyle ilgili temel bir sorun değil mi?
asmeurer

Asmeurer, evet # 2 numaramda ve daha sonra onların tartışılması. Ancak # 1 ve # 3 her ikisi de eşiğin altında belirleyici doğru sonuçlar vermek için Miller-Rabin'i bilinen bazlarla kullanma durumuydu. Bu nedenle, bu durumda "algoritma" (OP ile eşleşmek için gevşekçe terimini kullanarak) yanlıştı. # 4 olası bir ana test değil, fakat DW'nin belirttiği gibi, algoritma düzgün çalışıyor, sadece zor olan bir uygulama. Buna benzer bir duruma neden olduğu için dahil ettim: test etmek gerekiyor ve çalışmadan önce basit örneklerin ötesine ne kadar geçiyorsunuz?
DanaJ

Yazınızın bazıları, bazılarının uymadığı sırada soruya uyuyor gibi görünüyor (cf @ DW'nin yorumu). Lütfen soruyu yanıtlamayan örnekleri (ve diğer içerikleri) kaldırın.
Raphael

7

Fisher-Yates-Knuth karıştırma algoritması (pratik) bir örnek ve bu sitenin yazarlarından birinin yorum yaptığı bir örnek .

Algoritma, verilen bir dizinin rastgele permütasyonunu şu şekilde üretir:

 // 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

Bir "naif" algoritma olabilir:

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

Döngüde değiştirilecek eleman mevcut tüm elemanlardan seçilir. Ancak bu , permütasyonların taraflı örneklemesini üretir (bazıları aşırı temsil edilir vb.).

Aslında, basit bir (veya saf) sayım analizi kullanarak, balıkçı yates-knuff shuffling ile bir kişi ortaya çıkabilir .

nn!=n×n1×n2..nn1

Karıştırma algoritmasının doğru olup olmadığını ( önyargılı ya da değil ) doğrulamanın ana problemi , istatistiklerden dolayı çok sayıda numunenin gerekli olmasıdır. Yukarıda bağladığım kodlama makalesi tam olarak bunu (ve gerçek testlerle) açıklar.


1
Bir karıştırma algoritması için örnek doğruluğunu kanıtlamak için buraya bakın .
Raphael

5

En iyi örnek (okudum: en çok yaralandığım şey) şimdiye kadar gördüğüm kadarıyla collatz varsayımı ile ilgili. Problemlerden birinin iki sayı için aynı sayıya ulaşması için gereken minimum adım sayısını bulmak olduğu bir programlama yarışmasında (hatta birincilik için 500 dolarlık bir ödülle) bulundum. Tabii ki çözüm, her ikisi de daha önce görülen bir şeye erişene kadar sırayla adım atmak. Bize bir dizi numara verildi (sanırım 1 ile 1000000 arasındaydı) ve collatz sıfatının 2 ^ 64'e kadar doğrulandığını söyledi, böylece verilen tüm numaraların sonunda 1'de birleşeceğini söyledi. 32-bit kullandım. Bununla birlikte adımlarını yapmak için tamsayılar. Anlaşılan, 1 - 1000000 arasında (170 bin bir şey) belirsiz bir sayının olduğu ve 32-bit bir tamsayı'nın zaman içinde taşmasına neden olacağı ortaya çıkmıştır. Aslında, bu sayılar oldukça nadir görülür 2 ^ 31 Taşma olmamasını "sağlamak" için sistemimizi 1000000'den büyük BÜYÜK rakamlar için test ettik. Taşma nedeniyle test etmediğimiz çok daha küçük bir sayı ortaya çıkıyor. Çünkü "uzun" yerine "int" kullandım, 500 dolarlık bir ödül yerine sadece 300 dolarlık bir ödül aldım.


5

Sırt Çantası 0/1 sorun hemen hemen tüm öğrencilerin açgözlü bir algoritma ile çözülebilir olduğunu düşünüyorum biridir. Bu, daha önce bir açgözlü algoritmanın çalıştığı Sırt Çantası'nın problem versiyonu olarak bazı açgözlü çözümler gösterirseniz daha sık olur .

Bu problemler için, sınıfta , herhangi bir şüpheyi ortadan kaldırmak ve açgözlü sorun versiyonuna ilişkin Knapsack 0/1 ( dinamik programlama ) için kanıt göstermeliyim . Aslında, iki kanıt da önemsiz değildir ve öğrenciler muhtemelen onları çok yararlı bulmaktadır. Ayrıca, CLRS 3ed , Bölüm 16, Sayfa 425-427'de bununla ilgili bir yorum var .

Sorun: Bir mağazayı soyan hırsız ve sırt çantasına azami W ağırlığı taşıyabilir. Orada n öğeler var ve bu madde wi ağırlığında ve vi dolar değerinde. Hırsız hangi eşyaları almalı? kazancını arttırmak için ?

Sırt çantası 0/1 sorun : Kurulum aynı, ancak eşyalar daha küçük parçalara bölünmeyebilir , bu nedenle hırsız bir öğeyi almaya veya bırakmaya karar verebilir (ikili seçim), ancak öğenin bir kısmını almayabilir .

Ve öğrencilerden açgözlü versiyon problemiyle aynı fikri takip eden bazı fikirler veya algoritmalar alabilirsiniz.

  • Torbanın toplam kapasitesini alın ve mümkün olan en değerli nesneyi koyun ve daha fazla nesne koyana kadar bu yöntemi yineleyin, çünkü torba dolu veya torbanın içine koymak için eşit ağırlıkta daha az nesne bulunmaz.
  • Başka bir yanlış yol düşünmek: hafif eşyaları koymak ve bunları en düşük fiyata en düşük seviyeye çıkarmak.
  • ...

Sizin için yararlı mı Aslında, jeton sorununun bir sırt çantası problemi versiyonu olduğunu biliyoruz . Ancak, sırt çantası ormanlarının problemlerinde daha fazla örnek var, örneğin, Knapsack 2D hakkında ne düşünüyorsunuz (bu, mobilya yapmak için odun kesmek istediğinizde gerçekten faydalıdır , şehrimde yerel olarak gördüm), bunun çok yaygın olduğunu düşünüyorum. açgözlü burada da çalışır, ama değil.


Açgözlülük kabul edilmiş cevapta zaten ele alınmıştı , ancak özellikle Sırt Çantası sorunu bazı tuzaklar kurmak için çok uygun.
Raphael

3

Yaygın bir hata, karıştırma algoritmalarını yanlış uygulamaktır. Wikipedia'daki tartışmaya bakın .

n!nn(n1)n


1
Bu iyi bir hata, ancak test bir karıştırma algoritmaları için geçerli olmadığından test vakalarını sezgisel olarak kandırmanın iyi bir örneği değildir (rastgeledir, peki nasıl test edersiniz? Bunu çıkışa bakmaktan nasıl anlarsınız?)
DW

Elbette istatistiksel olarak test edersiniz. Düzgün rastgelelik "çıktıda her şey olabilir" den uzaktır. Eğer bir program bir zar taklit etsin demiş olsaydı sana 100 3'ü verirse şüphelenmez miydin?
Per Alexandersson

Yine, öğrencinin "bazı test vakalarını el ile dene" dersinden bahsettiğinden bahsediyorum. Bir çok öğrencinin, deterministik bir algoritmanın doğru olup olmadığını kontrol etmenin bunun makul bir yol olduğunu düşündüğünü gördüm, ancak bir karışma algoritmasının doğru olup olmadığını test etmenin iyi bir yol olduğunu varsaymayacaklarından şüpheleniyorum (karışma algoritması rasgele olduğundan, belirli bir çıktının doğru olup olmadığını söylemenin bir yolu yoktur, her durumda, yararlı herhangi bir istatistiksel test yapmak için el ile yeterince örnek veremezsiniz). Bu nedenle, karışık algoritmaların ortak yanlış algıların giderilmesinde çok yardımcı olacağını sanmıyorum.
DW

1
@PerAlexandersson: Yalnızca bir shuffle oluştursanız bile, n> 2080 ile MT kullanarak gerçekten rastgele olamaz. Şimdi beklenenden sapma çok küçük olacak, bu yüzden muhtemelen umursamayacaksınız ... ama bu olsa bile geçerlidir dönemin çok daha azını üretiyorsunuz (asimet yukarıda belirtildiği gibi).
Charles

2
Bu cevap tarafından kullanımdan düşürülmüş görünüyor Nikos M.'nin daha ayrıntılı biri ?
Raphael

2

Standart kütüphaneye istatistik işlevleri getiren Pythons PEP450 ilgi çekici olabilir. Yazar Steven D'Aprano, standart python kütüphanesindeki varyansı hesaplayan bir işleve sahip olmanın gerekçesinin bir parçası olarak şöyle yazar:

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)

Yukarıdakilerin günlük testlerde doğru olduğu görülüyor:

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

Ancak her veri noktasına bir sabit eklemek, varyansı değiştirmemelidir:

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

Ve varyans hiçbir zaman negatif olmamalıdır :

>>> variance(data*100)
  -1239429440.1282566

Mesele sayısal ve konuyla ilgili hassasiyetin kaybolması ile ilgili. Maksimum hassasiyet istiyorsanız, işlemlerinizi belirli bir şekilde sipariş etmeniz gerekir. Naif bir uygulama yanlış sonuçlara yol açar, çünkü hassasiyet çok büyüktür. Bu, üniversitedeki sayısal dersimin ilgili olduğu konulardan biriydi.


1
n1

2
@Raphael: Adil olmasına rağmen, seçilen algoritmanın kayan nokta verileri için kötü bir seçim olduğu iyi bilinmektedir.

2
Bu sadece operasyonun uygulanmasıyla ilgili değil, nümerikler ve hassasiyetin nasıl kaybolduğu hakkında. Maksimum hassasiyet istiyorsanız, işlemlerinizi belirli bir şekilde sipariş etmeniz gerekir. Bu, üniversitedeki sayısal dersimin ilgili olduğu konulardan biriydi.
Christian,

Raphael'in doğru yorumuna ek olarak, bu örnekteki bir eksiklik, bir doğruluk kanıtının bu kusurdan kaçınmaya yardımcı olacağını düşünmüyorum. Kayan nokta aritmetiğinin inceliklerini bilmiyorsanız, bunun doğru olduğunu kanıtladığınızı düşünebilirsiniz (formülün geçerli olduğunu kanıtlayarak). Bu nedenle, öğrencilere algoritmalarının doğru olduğunu kanıtlamanın neden önemli olduğunu öğretmek için ideal bir örnek değildir. Öğrenciler bu örneği görselerdi, benim şüphem, bunun yerine "kayan nokta / sayısal hesaplamaların zor olduğu" dersini çizmeleriydi.
DW

1

Bu muhtemelen peşinde olduğunuz şey olmasa da, bazı küçük vakaları başka bir düşünce yapmadan anlamak ve test etmek kesinlikle yanlış bir algoritmaya yol açacaktır.

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

Önerilen çözüm :

int f(int n) {
   return 1;
}

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

Bu, "bazı küçük vakaları deneyin ve sonuçtan bir algoritma çıkarsın" yaklaşımı, (a) 'nın uygulanmasının hızlı olduğu bir algoritma ile baskının ortaya çıkacağı programlama yarışmalarında sık sık (burada olduğu kadar olmasa da) çoğalır. ) hızlı bir çalışma süresine sahip.


5
Bunun çok iyi bir örnek olduğunu sanmıyorum, çünkü çok az kişi bir polinomun bölenlerini 1'e dönerek bulmaya çalışırdı.
Brian S 15

1
nn3n

Bu, bölenler (veya başka bir caklulasyon) için sabit bir değerin geri döndürülmesi anlamında, bir soruna yanlış bir algoritmik yaklaşımın sonucu olabilir (örneğin bir istatistiksel problem veya algoritmanın son durumlarını ele almama). Ancak cevabın tekrarlanması gerekiyor
Nikos M.

@NikosM. Heh. Burada ölü bir atı atıyormuş gibi hissediyorum, ancak sorunun ikinci paragrafı “eğer algoritmaları denemek için düşünebilecekleri tüm köşe davaları da dahil olmak üzere birkaç örnek üzerinde doğru çalışıyorsa, o zaman algoritmanın yapması gerektiği sonucuna varırlar” diyor. Her zaman şunu soran bir öğrenci vardır: "Neden birkaç test durumunda deneyebilirsem algoritmamı doğrulamam gerekiyor?" Bu durumda, ilk 40 değer için (bir öğrenciden çok daha fazlası) muhtemelen denemek muhtemel), 1'i döndürmek doğru, bana öyle geliyor ki OP'nin aradığı şey bu.
Rick Decker

Tamam, evet, ama bu ifadede olduğu gibi önemsizdir (tipik olarak doğru olabilir), ancak sorunun özünde değildir. Yine de yeniden düzenlemeye ihtiyacı var
Nikos M.
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.