123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164 |
- ;; https://projecteuler.net/problem=33
- ;; Digit cancelling fractions
- ;; Problem 33
- ;; The fraction 49/98 is a curious fraction, as an
- ;; inexperienced mathematician in attempting to simplify it
- ;; may incorrectly believe that 49/98 = 4/8, which is
- ;; correct, is obtained by cancelling the 9s.
- ;; We shall consider fractions like, 30/50 = 3/5, to be
- ;; trivial examples.
- ;; There are exactly four non-trivial examples of this type
- ;; of fraction, less than one in value, and containing two
- ;; digits in the numerator and denominator.
- ;; If the product of these four fractions is given in its
- ;; lowest common terms, find the value of the denominator.
- (import
- (except (rnrs base) let-values map)
- (only (guile)
- lambda* λ)
- (ice-9 match)
- ;; (srfi srfi-69) ; hash tables
- (srfi srfi-1) ; reduce
- (contract)
- (lib math)
- (lib print-utils))
- (define-with-contract make-fraction
- (require (integer? numer)
- (integer? denom)
- (not (zero? denom)))
- (ensure (pair? <?>))
- (λ (numer denom)
- (cons numer denom)))
- (define fraction-numer
- (λ (frac)
- (car frac)))
- (define fraction-denom
- (λ (frac)
- (cdr frac)))
- (define fraction?
- (λ (frac)
- (pair? frac)))
- (define-with-contract fraction-reduce
- (require (fraction? frac))
- (ensure (fraction? frac))
- (λ (frac)
- (let ([reduced-fraction
- (/ (fraction-numer frac)
- (fraction-denom frac))])
- (make-fraction (numerator reduced-fraction)
- (denominator reduced-fraction)))))
- (define-with-contract common-digit-in-terms
- (require (fraction? frac))
- (ensure (or (integer? <?>)
- (boolean? <?>)))
- (λ (frac)
- "Get the first common digit of the numerator and denominator
- of a fraction."
- (let ([numer-digits (digits (fraction-numer frac))]
- [denom (fraction-denom frac)])
- (let iter ([digits° numer-digits])
- (cond
- [(null? digits°) #f]
- [(contains-digit? denom (car digits°)) (car digits°)]
- [else (iter (drop digits° 1))]))
- ;; any
- #;(reduce (λ (cur acc)
- (or acc cur))
- #f
- ;; check if contains digits
- (map (λ (digit)
- (contains-digit? denom digit))
- numer-digits)))))
- (define trivial?
- (λ (frac)
- (or
- (and (divides? 10 (fraction-numer frac))
- (divides? 10 (fraction-denom frac)))
- (= (fraction-numer frac)
- (fraction-denom frac)))))
- (define cancelling-fractions
- (let iter ([numer 10] [denom 10] [found-fractions '()])
- (cond
- [(> denom 99)
- found-fractions]
- [(> numer denom) ; avoid duplicates
- (iter 10 (+ denom 1) found-fractions)]
- [(> numer 99)
- (iter 10 (+ denom 1) found-fractions)]
- [(not (trivial? (make-fraction numer denom)))
- (let ([common-digit
- (common-digit-in-terms (make-fraction numer denom))])
- #;(print numer "and" denom
- "have the following digit in common:"
- common-digit)
- (cond
- [common-digit
- (cond
- [(zero? (remove-digit denom common-digit))
- (iter 10 (+ denom 1) found-fractions)]
- [else
- (let ([wrongly-simplified-fraction
- (make-fraction
- (remove-digit numer common-digit)
- (remove-digit denom common-digit))])
- (cond
- [(= (/ numer denom)
- (/ (remove-digit numer common-digit)
- (remove-digit denom common-digit)))
- (iter (+ numer 1)
- denom
- (cons (make-fraction numer denom)
- found-fractions))]
- [else
- (iter (+ numer 1)
- denom
- found-fractions)]))])]
- [else
- (iter (+ numer 1)
- denom
- found-fractions)]))]
- [else
- (iter (+ numer 1)
- denom
- found-fractions)])))
- (print cancelling-fractions)
- (print
- (let ([frac
- (reduce (λ (frac acc)
- (make-fraction (* (fraction-numer frac)
- (fraction-numer acc))
- (* (fraction-denom frac)
- (fraction-denom acc))))
- 1
- cancelling-fractions)])
- (/ (fraction-numer frac)
- (fraction-denom frac))))
|