Algorithmic Toolbox 算法基础学习笔记(第二周 习题节选)

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 m2, 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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值