2017-01-17 2 views
2

Je lis SICP et faire de l'exercice 2.5:SICP Exercice 2.5 - sélecteur se comporte inconstante

Exercise 2.5. Montrer que nous pouvons représenter des paires de nombres entiers non négatifs en utilisant uniquement des nombres et des opérations arithmétiques si nous représentons la paire a et b en tant qu'entier représentant le produit 2^a*3^b. Donner les définitions correspondantes des procédures cons, car, et cdr.

Voici ma solution:

;;; Exercise 2.5 
;;; ============ 

(define (cons x y) 
    (* (expt 2 x) 
    (expt 3 y))) 

(define (car z) 
    ; n is a power of 2, which is greater than z 
    (let ((n (expt 2 (ceiling (/ (log z) (log 2)))))) 
    (/ (log (gcd z n)) (log 2)))) 

(define (cdr z) 
    ; n is a power of 3, which is greater than z 
    (let ((n (expt 3 (ceiling (/ (log z) (log 2)))))) 
    (/ (log (gcd z n)) (log 3)))) 

Mon code fonctionne bien avec des cas de test relativement faible:

(define x 12) 
(define y 13) 
(define z (cons x y)) 

(car z) 
;Value: 12. 
(cdr z) 
;Value: 12.999999999999998 

Cependant, il produit des résultats incorrects lorsque le nombre grossit:

(define x 12) 
(define y 14) 
(define z (cons x y)) 

(car z) 
;Value: 12. 
(cdr z) 
;Value: 2.8927892607143724 <-- Expected 14 

Je veux savoir quel est le problème avec ma mise en œuvre. Y a-t-il un problème avec l'algorithme? L'idée est que le plus grand constructeur commun de z = 2^x * 3^y et n (une puissance de 2 qui est supérieure à z) est exactement 2^x.

Si mon algorithme est correct, cette incohérence est-elle causée par une erreur d'arrondi et/ou un débordement?

+0

Il y a une asymétrie à vos définitions qui semble erronée. Je suppose que vous avez écrit 'cdr' en utilisant copier-coller. – molbdnilo

+0

@molbdnilo Oui je l'ai fait, car je pense qu'ils sont analogues. 'Car' et' cdr' utilisent '(plafond (/ (log z) (log 2)))' car 'z = 2^x * 3^y',' log_2 (z)' serait plus grand que à la fois 'x' et' y', alors que 'log_3 (z)' est seulement garanti supérieur à 'y'. –

+0

@SunQingyao J'ai juste changé '2' en' 3' dans la définition de 'cdr' et j'ai 14.0 au lieu de 2.89 donc ça semble avoir un effet. – Sylwester

Répondre

2

Une solution consiste à éviter les nombres à virgule flottante.

Tenir compte max-power-dividing qui trouve l'exposant maximal k tel que p^k divise n:

(define (max-power-dividing p n) 
    (if (zero? (remainder n p)) 
     (+ 1 (max-power-dividing p (/ n p))) 
     0)) 

Ensuite, nous pouvons écrire:

(define (car z) (max-power-dividing 2 z)) 
(define (cdr z) (max-power-dividing 3 z)) 

Pour autant que je peux dire, votre solution utilise la bonne idée , mais le calcul en virgule flottante se brise pour les grands nombres.

+0

Merci d'avoir fourni une autre solution à cette question. Cependant, je voudrais améliorer ma solution, plutôt que d'utiliser simplement la solution des autres. Quoi qu'il en soit, le débogage est la moitié du plaisir de coder. Donc, s'il vous plaît dites-moi comment forcer MIT-Scheme à ne pas utiliser le calcul en virgule flottante (De plus, mon algorithme semble avoir un ordre de croissance thêta-de-log (n), alors que le teta-of-n de croissance ...) –

+0

@ SunQingyao Je m'attends à 'log' est nécessairement à virgule flottante, mais vous pouvez vérifier les docs. quant à l'efficacité, vous pouvez améliorer le code donné ici, par une astuce standard de * doubler * à chaque étape de l'itération. –