Algorithmic Toolbox 算法基础学习笔记
例如:【第二周习题 节选】Fibonacci sequence, Pisano period, GCD and LCM文章目录
前言
选取部分有代表性的习题和C++代码实现2. Last Digit of a Large Fibonacci Number
2.1. Problem Statement
- Task. Given an integer 𝑛, find the last digit of the
𝑛
t
h
𝑛^{th}
nth
Fibonacci number 𝐹 𝑛 𝐹_𝑛 Fn (that is, 𝐹 𝑛 m o d 10 𝐹_𝑛 mod 10 Fnmod10). - Input Format. The input consists of a single integer 𝑛.
- Constraints. 0 ≤ 𝑛 ≤ 1 0 7 10^7 107
- Output Format. Output the last digit of 𝐹𝑛.
- Sample 1:
F 3 = 2 F_3 = 2 F3=2
Input:
3
Output:
2
- Sample 2:
F 331 = 668996615388005031531000081241745415306766517246774551964595292186469. F_{331} = 668 996 615 388 005 031 531 000 081 241 745 415 306 766 517 246 774 551 964 595 292 186 469. F331=668996615388005031531000081241745415306766517246774551964595292186469.
Input:
331
Output:
9
2.1. C++ Implementation
本题难度不大。只需将naive算法中for循环的current变量求模即可确保算法运行时不会超栈。
#include <iostream>
int get_fibonacci_last_digit_naive(int n) {
if (n <= 1)
return n;
int previous = 0;
int current = 1;
for (int i = 0; i < n - 1; ++i) {
int tmp_previous = previous;
previous = current;
current = tmp_previous + current;
}
return current % 10;
}
int get_fibonacci_last_digit_fast(int n) {
if (n <= 1)
return n;
int previous = 0;
int current = 1;
for (int i = 0; i < n - 1; ++i) {
int tmp_previous = previous;
previous = current;
current = tmp_previous + current;
current = current % 10; //to avoid integer overflow while save the last digit
}
return current;
}
int main() {
int n;
std::cin >> n;
//int c = get_fibonacci_last_digit_naive(n);
int d = get_fibonacci_last_digit_fast(n);
//std::cout << c << '\n';
std::cout << d << '\n';
}
4. Least Common Multiple
4.1. Problem Statement
-
Task. Given two integers 𝑎 and 𝑏, find their least common multiple.
-
Input Format. The two integers 𝑎 and 𝑏 are given in the same line separated by space.
-
Constraints. 1 ≤ 𝑎, 𝑏 ≤ 1 0 7 10^7 107
-
Output Format. Output the least common multiple of 𝑎 and 𝑏.
-
Sample 1:
Among all the positive integers that are divisible by both 6 and 8 (e.g., 48, 480, 24), 24 is the smallest one
Input:
6 8
Output:
24
- Sample 2:
Input:
761457 614573
Output:
467970912861
4.2. C++ Implementation
由于本题n的值较大,采用Naive 算法耗时过长。通过小学知识可知a和b最小公约数等于gcd(a, b) * (a / gcd(a, b)) * (b / gcd(a, b)) (即 a和b的最大公约数 与 a和b除以他们最大公约数的商 的乘积)。我们可以利用该规律写efficient算法,时间复杂度为 O ( l o g ( a + b ) ) O(log(a+b)) O(log(a+b)) (gcd欧式算法的复杂度)
#include <iostream>
long long lcm_naive(int a, int b) {
for (long l = 1; l <= (long long) a * b; ++l)
if (l % a == 0 && l % b == 0)
return l;
return (long long) a * b;
}
//find the greatest common divisor
int gcd_fast(int a, int b) {
if (b == 0) { return a; }
return gcd_fast(b, a%b);
}
long long lcm_fast(int a, int b) {
int gcd = gcd_fast(a, b);
a = a / gcd;
b = b / gcd;
return (long long)gcd * a * b;
}
int main() {
int a, b;
std::cin >> a >> b;
//std::cout << lcm_naive(a, b) << std::endl;
std::cout << lcm_fast(a, b) << std::endl;
return 0;
}
5. Least Common Multiple
5.1. Problem Statement
-
Task. Given two integers 𝑛 and 𝑚, output 𝐹 𝑛 𝐹_𝑛 Fn mod 𝑚 (that is, the remainder of 𝐹 𝑛 𝐹_𝑛 Fn when divided by 𝑚).
-
Input Format. The input consists of two integers 𝑛 and 𝑚 given on the same line (separated by a space).
-
Constraints. 1 ≤ 𝑛 ≤ 1 0 14 10^{14} 1014, 2 ≤ 𝑚 ≤ 1 0 3 10^3 103
-
Output Format. Output 𝐹 𝑛 𝐹_𝑛 Fn mod 𝑚.
-
Sample 1:
𝐹 239 𝐹_{239} F239 mod 1000 = 39679027332006820581608740953902289877834488152161 ( m o d 1000 ) = 161. 1 000 = 39 679 027 332 006 820 581 608 740 953 902 289 877 834 488 152 161 (mod 1 000) = 161. 1000=39679027332006820581608740953902289877834488152161(mod1000)=161.
Input:
239 1000
Output:
161
- Sample 2:
𝐹 2816213588 𝐹_{2 816 213 588} F2816213588 does not fit into one page of this file, but 𝐹 2816213588 𝐹_{2 816 213 588} F2816213588 mod 239 = 151.
Input:
2816213588 239
Output:
151
5.2. Hint
通过观察可知,对于任何整数
m
≥
2
m \geq 2
m≥2,
F
i
F_i
Fi mod m 的数列是周期性循环的。该周期被称作 Pisano period.
5.3. C++ Implementation
由于n数值过大,运行naive算法会导致超栈。 思路:先找到m的Pisano period,再计算n在该周期内的index对应的模。代码如下(示例):
#include <iostream>
long long get_fibonacci_huge_naive(long long n, long long m) {
if (n <= 1)
return n;
long long previous = 0;
long long current = 1;
for (long long i = 0; i < n - 1; ++i) {
long long tmp_previous = previous;
previous = current;
current = (tmp_previous + current)%m;
}
return current % m;
}
// Calculate and return Pisano Period
// The length of a Pisano Period for
// a given m ranges from 3 to m * m
long pisano(long long m) {
long prev = 0;
long curr = 1;
long res = 0;
if (m == 1) { res = 1; }
else{
for (int i = 0; i < m*m; i++) {
long temp_curr = curr;
curr = (prev + temp_curr) % m;
prev = temp_curr;
if (prev == 0 && curr == 1) {
res = i + 1;
break;
}
}
}
return res;
}
long long get_fibonacci_huge_fast(long long n, long long m) {
long long pisano_period = (long long)pisano(m);//find pisano period of m
long long result = get_fibonacci_huge_naive(n%pisano_period, m);//calculate Fib(n%pisano_period) mod m
return result;
}
int main() {
long long n, m;
std::cin >> n >> m;
std::cout << get_fibonacci_huge_fast(n, m) << '\n';
//std::cout << get_fibonacci_huge_naive(n, m) << '\n';
}
6. Last Digit of the Sum of Fibonacci Numbers
6.1. Problem Statement
-
Task. Given an integer 𝑛, find the last digit of the sum 𝐹 0 + 𝐹 1 + ⋅ ⋅ ⋅ + 𝐹 𝑛 𝐹_0 + 𝐹_1 + · · · + 𝐹_𝑛 F0+F1+⋅⋅⋅+Fn.
-
Input Format. The input consists of a single integer 𝑛.
-
Constraints. 1 ≤ 𝑛 ≤ 1 0 14 10^{14} 1014
-
Output Format. Output the last digit of 𝐹 0 + 𝐹 1 + ⋅ ⋅ ⋅ + 𝐹 𝑛 𝐹_0 + 𝐹_1 + · · · + 𝐹_𝑛 F0+F1+⋅⋅⋅+Fn.
-
Sample 1:
𝐹 0 + 𝐹 1 + 𝐹 2 + 𝐹 3 = 0 + 1 + 1 + 2 = 4. 𝐹_0 + 𝐹_1 + 𝐹_2 + 𝐹_3 = 0 + 1 + 1 + 2 = 4. F0+F1+F2+F3=0+1+1+2=4.
Input:
3
Output:
4
- Sample 2:
𝐹 0 + 𝐹 1 + . . . + 𝐹 99 + 𝐹 100 𝐹_0 + 𝐹_1 + ...+ 𝐹_{99} + 𝐹_{100} F0+F1+...+F99+F100 the sum is equal to 927 372 692 193 078 999 175, the last digit is 5.
Input:
100
Output:
5
6.2. Hint
思路:由上题可知,斐波那契数的最后一位数字出现在长度为 60 的序列中(因为 10 的 pisano peiod 是 60)。不管 n 有多大,它的最后一位数字都会出现在序列中的某个地方。此外斐波那契数列的前n项和公式为:
- Sum of n t h n^{th} nth Fibonacci series = F(n+2) -1
因此,可通过以下步骤求前n项和的个位数:
- 已知Pisano period of module 10 = 60
- Let n+2 mod (60) = m, then find F(m) mod(10)-1
6.3. C++ Implementation
Instead of calculating the last digit of the sum of all numbers in the range [0, N], we simply calculate the sum until the remainder given that the Pisano period for Fi mod 10 is 60.
#include <iostream>
int fibonacci_sum_naive(long long n) {
if (n <= 1)
return n;
long long previous = 0;
long long current = 1;
long long sum = 1;
for (long long i = 0; i < n - 1; ++i) {
long long tmp_previous = previous;
previous = current;
current = tmp_previous + current;
sum += current;
}
return sum % 10;
}
int fibonacci_sum_fast(long long n) {
//initiator
long long previous = 0;
long long current = 1;
if (n == 0) {
return 0;
}
else if (n == 1) {
return 1;
}
else {
long long remainder = n % 60;// Pisano period for % 10 is 60
if (remainder == 0) {
return 0;// Checking the remainder
}
//Golden Rule: s(n) = 2F(n) + F(n-1) - 1 = F(n+2)-1
// The loop will range from 2 to two terms after the remainder: e.g. 0, 1, 2 for i = 2
for (long long i = 2; i < remainder + 3; i++)
{
long long sum = (previous + current) % 100;
previous = current;
current = sum;
}
long long s = (current - 1)%10;//must subtract 1 because we add an additional 1 at the initiator!!!
/*
if (s == -1)
s += 10;
*/
return s;
}
}
int main() {
long long n = 0;
std::cin >> n;
//std::cout << fibonacci_sum_naive(n);
/*
for (int i = 0; i < 60; i++) {
std::co
*/
std::cout << fibonacci_sum_fast(n) << std::endl;
}
总结
参考:
Algorithmic toolbox
Last digit of sum of numbers in the given range in the Fibonacci series