-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathproblem6.hs
95 lines (59 loc) · 2 KB
/
problem6.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
module Main where
-- Problem 6:
-- The sum of the squares of the first ten natural numbers is,
-- 1^2 + 2^2 + ... + 10^2 = 385
-- The square of the sum of the first ten natural numbers is,
-- (1 + 2 + ... + 10)^2 = 552 = 3025
-- Hence the difference between the sum of the squares of the first
-- ten natural numbers and the square of the sum is 3025 − 385 = 2640.
-- Find the difference between the sum of the squares of the first one
-- hundred natural numbers and the square of the sum.
{-
Solution:
Let sumsq n = 1^2 + ... + n^2
I haven't been able to find a closed-form expression for this, like
there is for (sum n). However, there is a nice recurrence relation for
(sum n)^2 - (sumsq n)
First note the obvious recurrences
sum 1 = 1
sum n = n + sum (n-1), n > 1
and
sumsq 1 = 1
sumsq n = n^2 + sumsq (n-1), n > 1
So let
d n = (sum n)^2 - sumsq n
Therefore we see that
d 1 = (sum 1)^2 - sumsq 1 = 0
and for n > 1 we have
d n
= (sum n)^2 - sumsq n
= (n + sum (n-1))^2 - (n^2 + sumsq (n-1))
= n^2 + 2n(sum (n-1)) + (sum (n-1))^2 - n^2 - sumsq (n-1)
= 2n(sum (n-1)) + (sum (n-1))^2 - sumsq (n-1)
= 2n(sum (n-1)) + d (n-1)
Now, we know that
sum m = m (m + 1) / 2
so
sum (n-1) = (n-1) ((n-1) + 1) / 2 = n (n - 1) / 2
and do we get
d n = 2n(sum (n-1)) + d (n-1)
= 2n(n(n-1)/2) + d (n-1)
= n^2 (n-1) + d (n-1)
With this recurrence, we can compute (d n) using n-1 additions,
n-1 subtractions, and 2(n-1) multiplications. For (d 100), this
is isn't much work at all.
-}
-- Here is a tail-recursive version.
d :: Integer -> Integer
d 1 = 0
d n = d' 1 0
where d' m last | m > n = last
| otherwise = d' (m+1) (m*m*(m-1) + last)
-- Here is a simpler version that uses a non-tail call. Because of
-- lazy evaluation, my intuition about which is better isn't that well
-- formed. This version has the benefit of being much easier to read.
dr :: Integer -> Integer
dr 1 = 0
dr n = n*n*(n-1) + d (n-1)
main :: IO ()
main = print $ d 100