【算法】算法引论

6 篇文章 1 订阅

算法引论

参考 : 《数据结构与算法分析》. Mark Allen Weiss. 机械工业出版社

​ 第一章 : 引论

P和NP

P对NP问题是Steve Cook于1971年首次提出。“P/NP问题”,这里的P指多项式时间(Polynomial),一个复杂问题如果能在多项式时间内解决,那么它便被称为P问题,这意味着计算机可以在有限时间内完成计算;NP指非确定性多项式时间(nondeterministic polynomial),一个复杂问题不能确定在多项式时间内解决,假如NP问题能找到算法使其在多项式时间内解决,也就是证得了P=NP。比NP问题更难的则是NP完全NP-hard,如围棋便是一个NP-hard问题。2010年8月7日,来自惠普实验室的科学家Vinay Deolalikar声称已经解决了“P/NP问题” ,并公开了证明文件。

  • P类包含的是在多项式时间内可以解决的问题
  • NP类包含的是在多项式时间内“可验证”的问题。是指给定某一解决方案的“证书”,就能在问题输入规模的多项式时间内,验证该“证书”是正确的。
  • P中任何问题都属于NP (即为NP类的子集),因为若某一问题属于P,则可以在不给出证书的情况下,在多项式时间内解决它。
  • 通俗地说,如果一个问题属于NP,且与NP中的任何问题一样“难”的,则说它属于NPC类,即NP完全的(Non-deterministic Polynomial Complete)。
    • 若NP 中的所有问题都能在多项式时间内归约为搜索问题A,那么则称问题A是NP- 完全的 (NPC)。
  • N P = P + N P C NP = P + NPC NP=P+NPC

处理NP完全问题

  • 在实践中,我们必须为这些各种各样的问题找到某种解决办法,因此人们对解决这些问题非常感兴趣。

  • (1)修改问题并寻找一种“近似”算法来给出接近但并非最佳的解。

  • (2)给出一种能够有效解决实际应用中所出现的问题的实例算法,但对于最坏情况下的输入,这种算法仍然是无法找到问题的解。这种方法最著名的例子是解决整数线性规划问题的程序,尽管它们有可能需要指数级别的时间,但实际应用中的输入数据也显然不是最坏情况下的输入。

  • (3)使用一种叫做“回溯法”的技术来避免检查所有可能的解,以期找到尽可能“高效”的指数级别算法。

级数

  • ∑ i = 0 N A i = A N + 1 − 1 A − 1 \sum\limits_{i=0}^NA^i = \frac{A^{N+1} -1}{A-1} i=0NAi=A1AN+11​​​

  • ∑ i = 0 N A i ≤ 1 1 − A , 若 0 < A < 1 \sum\limits_{i=0}^NA^i \leq \frac{1}{1-A}, 若0<A<1 i=0NAi1A1,0<A<1​​​​​

  • ∑ i = 1 N i = N ( N + 1 ) 2 \sum\limits_{i=1}^N i = \frac{N(N+1)}{2} i=1Ni=2N(N+1)​​​

  • ∑ i = 0 N A 2 = N ( N + 1 ) ( 2 N + 1 ) 6 \sum\limits_{i=0}^N A^2 = \frac{N(N+1)(2N+1)}{6} i=0NA2=6N(N+1)(2N+1)​​​

  • ∑ i = 1 N i k ≈ N k + 1 ∣ k + 1 ∣ , k ≠ − 1 \sum\limits_{i=1}^N i^k \approx \frac{N^{k+1}}{|k+1|} , k\neq -1 i=1Nikk+1Nk+1,k=1​​​​​

  • 调和级数 H N = ∑ i = 1 N 1 i ≈ l o g e N H_N = \sum\limits_{i=1}^{N} \frac{1}{i} \approx log_e N HN=i=1Ni1logeN​ 其中近似值的误差趋于 γ ≈ 0.57721566 \gamma \approx 0.57721566 γ0.57721566​​ 该值称为欧拉常数

在计算机科学中,除非有特别的声明,否则所有的对数都是以2为底的。

比如 : l o g N log N logN 实际就是 l o g 2 N log_2 N log2N

递归

  1. 递归逆序打印输出数。写一个递归程序,逆序打印一个正整数的各位数字。
public class Solution {
    
//	正序输出
	void PrintOut (int n) { 
		if(n>9) {
			PrintOut(n/10);	
		}
		int m = n%10;
		System.out.print(m+" ");
	}
	
//	反序输出
	void printOut(int n){ 
		if ( n!=0) {
			int m = n%10; //把低位先赋值给m
			System.out.print(m+" "); //先输出低位数
			printOut(n/10);  //递归调用, 逐渐输出
		}
	}
			
	public static void main(String[] args) {
		Solution a = new Solution();
		a.PrintOut(1200);
		System.out.println();
		a.printOut(1200);
		}
	
}

输出结果 :

1 2 0 0 
0 0 2 1 

或者 :

public class Solution {	
//    正序输出
	void PrintOut(int n){ 
		if ( n!=0) {
			PrintOut(n/10);  //先递归调用, 再逐渐输出
			n = n%10; //把最高位先输出
			System.out.print(n+" "); //先输出低位数
		}
	}
	
	
//	反序输出
	void printOut(int n){ 
		if ( n!=0) {
			int m = n%10; //把低位先赋值给m
			System.out.print(m+" "); //先输出低位数
			printOut(n/10);  //递归调用, 逐渐输出
			//尾递归 : 如果在从递归调用返回时没有继续的操作要完成,那么这个递归方法就称为尾递归(tail recursive)
		}
	}
	

		
	public static void main(String[] args) {
		Solution a = new Solution();

		a.PrintOut(1200);
		System.out.println();
		a.printOut(1200);
		}	 	
}

输出 :

1 2 0 0 
0 0 2 1 
  1. 将欧几里德算法改写为递归算法。
public class Solution {

//	欧几里得算法 (求最大公约数)
	int Gcd(int m, int n) {
		int Rem;
		while(n>0) {
			Rem = m%n;
			m=n;
			n=Rem;
		}
		return m;	
	}
	
	
//	或者用递归实现欧几里得算法
	int gcd(int m, int n) {
		if(n>0) {
			return gcd(n,m%n);
		}else {
			return m;
		}
	}

	
	public static void main(String[] args) {
		Solution a = new Solution();
		int b1 = a.Gcd(16,8);
		int b2 = a.gcd(16,8);
		System.out.println(b1);
		System.out.println(b2);
		}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值