最长递增子序列

一.问题描述

设 X = <x_{k1},x_{k2}\ldots,x_{n}>是n个不同的实数序列,L的递增子序列L'= <a_{k1}, a_{k2}\ldots,a_{kn}>,其中x_{k1}<x_{k2}\ldots<x_{km}, 求最大的m值

二.问题求解

2.1 转化为LCS问题求解

设序列A = <a_{k1},a_{k2}\ldots,a_{n}>是序列 X = <x_{k1},x_{k2}\ldots,x_{n}>按递增顺序排好的序列,则A与L的最长公共子序列为L的最长递增子序列。这样把求最长递增子序列问题规约为最长公共子序列问题LCS了。

此解法的时间复杂度为:O(n^2)+O(nlogn)

2.2 使用动态规划求解

maxLength(i)为X中以x_{i}为末元素的最长递增子序列的长度,有如下的递推方程:

maxLength(i) = maxLength(j)+1   x_{i} < x_{j}, k<\neq j < i, maxLength(k) < maxLength(j)

maxLength(i) = 1

即在求以x_{i}为末元素的最长递增子序列长度时,对所有在x_{i}前面的元素x_{j},且x_{j}<x_{i},如果j的值存在,找出maxLength(j)的最大值,则maxLength(i) = maxLength(j)+1,否则maxLength(i) = 1

算法如下:

function lis_length( a )
    n := a.length
    q := new Array(n)
    for k from 0 to n:
        max := 0;
        for j from 0 to k, if a[k] > a[j]:
            if q[j] > max, then set max = q[j].
        q[k] := max + 1;
    max := 0
    for i from 0 to n:
        if q[i] > max, then set max = q[i].
    return max;

程序如下:

/*
**Descrition:	Returns the length of the longest increasing subsequence
**		Note that this is looking for the longest strictly increasing subsequence
**		This can be modified for other situations
**Author:	jia1546@163.com
**Date:		Oct 15, 2011
**Version:	1.0	
*/

#include <stdio.h>
#include <stdlib.h>

int lis(int *a, int n)
{
    int i, j;
    int *best, *prev;
    int max = 0;
    best = (int*)malloc(sizeof(int) * n);
    prev = (int*)malloc(sizeof(int) * n);

    for(i=0; i<n; i++)
    {
	best[i] = 1;
        prev[i] = i;
    }

    for(i=1; i<n; i++)
	for(j=0; j<i; j++)
 	    if(a[i] >= a[j] && best[i] < best[j] + 1)
	    {
		best[i] = best[j] + 1;
 		prev[i] = j;
	    }
    
    for(i=0; i<n; i++)
    	if(max < best[i])
	    max = best[i];

    free(best);
    free(prev);
    
    return max;
}

int main()
{
    int n;
    int *array;
    int i;
    while(scanf("%d",&n) != EOF)
    {
 	array = (int*)malloc(sizeof(int) * n);

	for(i=0; i<n; i++)
	    scanf("%d",array+i);
	printf("# of the lis is: %d\n", lis(array, n));
	
    }
    return 0;
}

时间复杂度: O(n^2)

2.2 改进的动态规划

在上一算法中,在计算maxLength(i)时,需要找出最大的maxLength(j),由于maxLength(j)无序,所以只能顺序查找x_{j} < x{i}的最大值maxLength(j),如果能让maxLength(j)有序,可以使用二分查找的方法降低复杂度到O(nlogn)

数组M[j]存储所有长度为j的子序列中最小末元素的X[k]的下标k

数组P[k]存储以X[k]为末元素的最长递增子序列中X[k]的前一元素的位置

算法如下:

L=0
for i=1,2,...n
    binary search for the largest positive j<=L
     such that X[M[j]] < X[i](or set j=0 if no such j exists)
    P[i] = M[j]
    if  j == L or X[i] < X[M[j+1]]
	M[j+1] = i
	L = max(L, j+1)

程序如下:

#include <vector>
using namespace std;
 
/* Finds longest strictly increasing subsequence. O(n log k) algorithm. */
void find_lis(vector<int> &a, vector<int> &b)
{
	vector<int> p(a.size());
	int u, v;
 
	if (a.empty()) return;
 
	b.push_back(0);
 
	for (size_t i = 1; i < a.size(); i++) 
        {
                // If next element a[i] is greater than last element of current longest subsequence a[b.back()], just push it at back of "b" and continue
		if (a[b.back()] < a[i]) 
                {
			p[i] = b.back();
			b.push_back(i);
			continue;
		}
 
                // Binary search to find the smallest element referenced by b which is just bigger than a[i]
                // Note : Binary search is performed on b (and not a). Size of b is always <=k and hence contributes O(log k) to complexity.    
		for (u = 0, v = b.size()-1; u < v;) 
                {
			int c = (u + v) / 2;
			if (a[b[c]] < a[i]) u=c+1; else v=c;
		}
 
                // Update b if new value is smaller then previously referenced value 
		if (a[i] < a[b[u]]) 
                {
			if (u > 0) p[i] = b[u-1];
			b[u] = i;
		}	
	}
 
	for (u = b.size(), v = b.back(); u--; v = p[v]) b[u] = v;
}
 
/* Example of usage: */
#include <cstdio>
int main()
{
	int a[] = { 1, 9, 3, 8, 11, 4, 5, 6, 4, 19, 7, 1, 7 };
	vector<int> seq(a, a+sizeof(a)/sizeof(a[0])); // seq : Input Vector
	vector<int> lis;                              // lis : Vector containing indexes of longest subsequence 
        find_lis(seq, lis);
 
        //Printing actual output 
	for (size_t i = 0; i < lis.size(); i++)
		printf("%d ", seq[lis[i]]);
        printf("\n");    
 
	return 0;
}

Ref

http://www.algorithmist.com/index.php/Longest_Increasing_Subsequence

http://en.wikipedia.org/wiki/Longest_increasing_subsequence

Majority Vote Algorithm

一个学妹问我的问题,让设计时间复杂度为O(nlogn)的算法,找出数组中出现次数超过数组长度一半的值。上网搜了之后发现,其实有更好的解法,Boyer and Moore's Voting Algorithm,降低时间复杂度到O(n)。

1.递归解法(Recursive Method)

function majority ( array A with numer_of_elements N)
{
    if N == 1:
        return A[0]
    let AL, AR be the first halves of A
    let ML = majority(AL)
    let MR = majority(AR)
    if neither half has a majority:
    	return "no majority"
    else:
	check whether either ML or MR is a majority element of A
        if so: return that element
        else: return "no majority"
}

时间复杂度:O(N*logN)

2. Boyer and Moore's Voting Algorithm

#include <stdio.h>
#include <stdlib.h>

int candidate_element(int *array, int n)
{
    int count = 1;
    int curr_candidate = 0;
    int i;
    for(i=0; i<n; i++)
    {
	if(array[i] == array[curr_candidate])
	    ++count;
	else
	    --count;
	if(count == 0)
	{
	    curr_candidate = i;
	    count = 1;
	}
    }
    return array[curr_candidate];
}

int is_majority_element(int *array, int n, int candidate)
{
    int cnt = 0;
    int i;
    for(i=0; i<n; i++)
	if(array[i] == candidate)
	    ++cnt;
    if(cnt > n/2)
	return 1;
    else
	return 0;
}

int main()
{
    int *array;
    int n;
    int i;
    int candidate;
    while(scanf("%d", &n) != EOF)
    {
	array = (int*)malloc(sizeof(int) * n);
        for(i=0; i<n; i++)
	    scanf("%d",array+i);
	
	candidate = candidate_element(array, n);
	if(is_majority_element(array, n, candidate))
	    printf("the majority element is: %d\n", candidate);
	else
	    printf("there is no majority element in the give array\n");
    }
    return 0;
}

时间复杂度:O(N)

形式化证明及分析见此文档: majority vote algorithm.pdf

 

矩阵二分乘求解递推数列

问题描述:A[k] = p*A[k-1] + q*A[k-2] 当k值很大时,如何快速求解A[k]的值?

这篇文章给出了很好的答案:http://hi.baidu.com/jzlikewei/blog/item/ad67cbb511b95ed836d3caa3.html

可以使用循环模拟递归运算以降低复杂度

 

Java Scanner 中next() 与nextLine()的区别

最近写程序,把Scanner next() 与 nextLine()混合使用时很容易出错,还是先了解下它们的区别吧。

关键在于:next() 方法遇见第一个有效字符(非空格,换行符)时,开始扫描,当遇见第一个分隔符或结束符(空格或换行符)时,结束扫描。这时使用nextLine(),继续读,有可能读入第一个字符是空格或换行符。

这篇文章的例子描述得很清楚。解析Scanner之next与nextLine区别.pdf

质因数分解

自己写的这个程序过于复杂,上网搜了之后发现可以有很简单的实现。

import java.io.*;
import java.util.*;

public class divide {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int i = 0;
		int j = 2;
		if(sc.hasNext())
			i = Integer.parseInt(sc.nextLine());
		System.out.printf("%d = ",i);
		do 
		{
			if (i % j == 0) 
			{
				System.out.printf("%d*",j);
				i = i / j;
				j = 2;
			} 
			else
				j++;
			if (i == 1)
				break;
		} while (true);
		System.out.printf("%c", '\b');
	}
}

Review of terms in Computation

一、并行计算(Parallel Computing)

并行算或称平行计算是相对串行计算来说的,所谓并行计算可以分为时间上和空间上的并行。时间上的并行是指采用流水线技术,而空间上的并行则指用多个处理器并发执行计算。并行计算科学中主要研究的是空间上的并行问题。从程序和算法设计人员角度来看,并行计算有可分为数据并行和任务并行。一般来说,因为数据并行主要是把一个大任务化解为相同的子任务,比任务并行容易处理。

空间上的并行导致了两类并行机的产生,按照Flynn分类法,计算机可分为单指令流单数据流(SISD),多指令多数据流(MIMD)。MIMD的机器又可分为:并行向量处理机(PVP),对称多处理机(SMP),大规模并行处理机(MPP),分布式共享存储处理机(DSM)。

并行计算是靠网络将各个处理机或处理器连接起来,一般有以下几种方式:

静态连接:处理单元间有着固定连接的网络,在程序执行期间,这种点到点的连接保持不变,典型的静态连接网络立方体网络,蝶形网络,洗牌交换网络等等。

动态连接:用交换开关构成,可按应用程序的要求动态的改变连接组态,典型的网络包括总线,交叉开关,和多级互连网络等。

二、分布式计算

分布式计算是近年提出的一种新的计算方式。它研究如何把一个需要非常巨大计算能力才能解决的问题分成许多小的部分,然后把这些部分分配给许多计算机进行处理,最后将这些计算结果综合起来得到最终结果。

分布式计算有以下优点:

1、稀有资源可以共享

2、通过分布式计算可以在多台计算机上平衡负载

3、可以把程序放在最适合它的计算机上

其中,共享稀有资源和平衡负载是分布式计算的核心思想之一

三、网格计算

实际上,网格计算是分布式计算的一种,如果说某些工作是分布式的,那么参加这项工作的一定不只是一台计算机,而是一个计算机网络,显然这种计算方式具有很强的数据处理能力,网格计算的实质就是组合共享资源并确保系统安全。

四、云计算

狭义云计算是指IT基础设施的交付和使用模式,指通过网络以按需、易扩展的方式获得所需的资源;广义云计算是指服务的交付和使用模式,指通过网络以按需易扩展的方式获得所需的服务。这种服务可以是IT和软件、互联网相关,也可以是其他服务。提供资源的网络被称为“云”,“云”中的资源在使用者看来是可以无限扩展的,并可以随时获取,按需使用,随时扩展,按使用付费。云计算的产业分为三层:云软件、云平台、云设备

大数阶乘运算

使用数组存储最终结果,并模拟进位

#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 1000	
int main()
{	
    int data[MAXSIZE+1];
    int digit ;
    int i,j,r,k;
    int n;
    while(scanf("%d",&n) != EOF)
    {
	for(i=1;i<=MAXSIZE;i++)
	    data[i] = 0;
	data[1] = 1;
	digit = 1;
	
	for(i=1; i<=n; i++)
	{
	    for(j=1;j<=digit;j++)
		data[j] *= i;
	    for(j=1;j<=digit;j++)
	    {
		if(data[j] > 10)
		{
		    for(r=1;r<=digit;r++)
		    {
		        if(data[digit] > 9)
			    digit++;
			data[r+1] += data[r]/10;
			data[r] = data[r]%10;
		    }
		}
	    }
	}
	for(k=digit;k>0;k--)
	    printf("%d",data[k]);
	printf("\n");
    }
    return 0;
}

ubuntu 下安装JDK JRE Eclipse Tomcat

Java JDK JRE,Eclipse Tomcat 都是常用的开发环境,但是配置他们却很繁琐,这个网站提供了安装及环境配置的基本步骤。

http://www.ylmf.net/ubuntu/tips/2010122016450.html

升级到Ubuntu 11.10后,安装方法稍有不同,见此文章:http://blog.csdn.net/lmyclever/article/details/6877425

Latex中添加url

由于Bitex出现时没有考虑Internet会对未来的research产生巨大影响,所以在Bitex引用中使用url同样费尽周折。

幸好这篇文章给出了完美的解决方案:http://win.ua.ac.be/~nschloe/content/bibtex-how-cite-website

注意:

@misc{ApacheHadoop,
  title        = {Welcome to Apache\^{TM} Hadoop},
  author    = {The Apache Software Foundation},
  howpublished= {\url{http://hadoop.apache.org/}},
  year      = {2011}
}

引用项不能出现空格,在正文中引用时使用\notice{ApacheHadoop}

Latex中添加参考文献

最近转到linux环境下,发现Latex排版工具Texlive用起来更加方便。今天尝试在文档中以更加简便的方式添加参考文献的引用,以前都是手动输入,格式很不正确,费时又费力。于是Google “how to cite a paper in latex”,于是在这篇文章帮助下,顺利实现。http://space.uibe.edu.cn/u1/ryang/latex-bib.html

注意:只有在正文中引用了相应文章,Reference下才会出现对应的参考文献条目。我没有引用文章却一直在找为什么Reference下没有出现文献数据库中的条目呢,唉!

可以根据dblp [paper title]来获得对应文献的BiTex条目,将它添加到引用文献数据库中即可。

在Texlive中编译时需要按顺序执行以下编译命令:

Latex -> Bitex -> Latex -> PdfTex -> PdfTex

PS: \bibliographystyle{}命令可以设置参考文献样式,具体见 http://www.zhuxiangs.com/?p=42