袋熊的树洞

日拱一卒,功不唐捐

0%

题目描述:一只青蛙一次可以跳上1级台阶,也可以跳上2级 …… 它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

先从最简单的情况说起。如果只有1级台阶,跳法只有一种。如果有2级台阶,那就有两种跳法:一种是分两次跳,每次跳1级;另一种就是一次跳2级。如果有3级台阶,那就有四种跳法:一种是分三次跳,每次跳1级;一种是先跳2级,再跳1级;一种是先跳1级,再跳2级;还有一种是一次跳3级。

接下来讨论一般情况。我们把n级台阶时的跳法看成n的函数,记为$f(n)$。根据前面分析,可以得到
$$
\begin{aligned}
f(1) & = 1 = 2^0 \
f(2) & = 2 = 2^{2-1} \
f(3) & = 4 = 2^{3-1}
\end{aligned}
$$

可以看出一个规律,那就是$f(n) = 2^{n-1}$,那么怎么证明呢?先分析下n级台阶怎么跳,青蛙第一次跳有n中选择,可以第一次跳1级,此时后面台阶的跳法数目等于$f(n-1)$,如果第一次跳2级,此时后面台阶的跳法数目等于$f(n-2)$,一直到第一次直接跳$n$级,此时剩余台阶数为0,则跳法数目为$f(0)$,这里约定$f(0)=1$,为了后面分析方便。综上可以得到一个递推公式:
$$
f(n) = f(n-1) + f(n-2) + \cdots + f(2) + f(1) + f(0)
$$
接下来用数学归纳法证明$f(n)=2^{n-1}$。如果前$n$项满足公式$f(n)=2^{n-1}$,那么对于$f(n+1)$,可以得到
$$
\begin{align}
f(n+1) &= f(n) + f(n-1) + \cdots + f(1) + f(0) \
& = 2^{n-1} + 2^{n-2} + \cdots + 2^0 + 1 \
& = \frac{1 - 2^n}{1 - 2} + 1 \
& = 2^n
\end{align}
$$
可看出$f(n+1)$仍然满足公式。知道通向公式后,剩下的实现就很简单了,贴上C++代码。

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
#include <iostream>
using namespace std;

class Solution {
public:
int jumpFloorII(int number) {
if (number == 0)
return 0;

int f = 1;
for (int i=1; i<number; i++)
f *= 2;

return f;
}
};

int main()
{
Solution solu;

cout << "f(0)= " << solu.jumpFloorII(0) << endl;
cout << "f(1)= " << solu.jumpFloorII(1) << endl;
cout << "f(2)= " << solu.jumpFloorII(2) << endl;
cout << "f(5)= " << solu.jumpFloorII(5) << endl;
return 0;
}

本文主要介绍如何将大量图片数据集合起来,然后生成一个SequenceFile,这么做的原因是避免Hadoop处理大量小文件导致性能下降。SequenceFile的key-value内容以及数据类型为:

1
2
key : 储存图片文件名,数据类型为Text
value : 储存图片数据,数据类型为ArrayPrimitiveWritable

为什么要用ArrayPrimitiveWritable来存储图片数据,主要是我想将图片数据用一个int数组来存储,数组元素就是图像的像素,数据范围在[0, 255],当然,你可以使用byte数组来存储图片数据。

Read more »

本文介绍了如何使用Java来获取指定目录下的所有文件。

获取指定目录下的所有文件

使用递归方法遍历目录,如果访问的是文件夹,则进入递归,否则将文件加入到文件列表中,同时可以设定遍历深度(从深度1开始)。

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
import java.io.File;
import java.util.ArrayList;

public class ListFiles {

/**
* 获取指定目录下的所有文件(不包括文件夹)
* @param path 文件夹的File对象或者文件夹路径字符串
* @param depth 遍历深度,从深度1开始
* @return 指定目录下所有的文件的File对象的集合
*/
public static ArrayList<File> getAllFiles(Object path, int depth) {
File directory = null;
if (path instanceof File) {
directory = (File)path;
} else {
directory = new File(path.toString());
}

ArrayList<File> files = new ArrayList<File>();
if (directory.isFile()) {
files.add(directory);
} else if (directory.isDirectory()) {
depth--;
if (depth >= 0) {
File[] fileList = directory.listFiles();
for (File file : fileList) {
files.addAll(getAllFiles(file, depth));
}
}
}

return files;
}
}

测试代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import org.junit.Test;

import java.io.File;
import java.util.ArrayList;

public class ListFilesTest {
@Test
public void getAllFiles() throws Exception {
String path = "/Users/luowanqian/Documents/Datasets/LFWCrop/lfwcrop_grey/faces";

ArrayList<File> files = ListFiles.getAllFiles(path, 1);
int n = 0;
for (File file : files) {
n++;
System.out.println(file.toString());
}
System.out.println("Total : " + n);
}

}

运行结果如下:

1
2
3
4
5
6
7
8
...
/Users/luowanqian/Documents/Datasets/LFWCrop/lfwcrop_grey/faces/Zoran_Djindjic_0004.pgm
/Users/luowanqian/Documents/Datasets/LFWCrop/lfwcrop_grey/faces/Zorica_Radovic_0001.pgm
/Users/luowanqian/Documents/Datasets/LFWCrop/lfwcrop_grey/faces/Zulfiqar_Ahmed_0001.pgm
/Users/luowanqian/Documents/Datasets/LFWCrop/lfwcrop_grey/faces/Zumrati_Juma_0001.pgm
/Users/luowanqian/Documents/Datasets/LFWCrop/lfwcrop_grey/faces/Zurab_Tsereteli_0001.pgm
/Users/luowanqian/Documents/Datasets/LFWCrop/lfwcrop_grey/faces/Zydrunas_Ilgauskas_0001.pgm
Total : 13233

Reference

  1. http://hw1287789687.iteye.com/blog/1946488
  2. http://blog.csdn.net/u013457382/article/details/51015728

最近下载了一个人脸数据库,里面的图片都是pgm格式,想提取每张图片的所有像素的像素值,初步设想是使用Java SE中读取图片。在使用Java SE中类来读取图片时,通常使用ImageIO包中的类来读取图片,但是有个缺点就是默认支持的图片格式很少,Java 8中javax.imageio支持的图片格式有:

从图中看出库仅仅支持JPEG、PNG、BMP、WBMP以及GIF这些图片格式。如果想读取其他格式(例如PGM)时应该怎么办?网上搜索到了一个Java ImageIO的扩展插件——TwelveMonkeys,安装后ImageIO就可以支持多种图片格式,这个有一个好处就是原先读取图片的代码不需要改变,读取图片还是使用 javax.imageio 包。

安装TwelveMonkeys

安装很简单,使用Maven可以很容易地添加到项目中,如果想让ImageIO支持JPEG和TIFF格式,可以在POM文件中添加下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
...
<dependencies>
...
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-jpeg</artifactId>
<version>3.3.1</version> <!-- Alternatively, build your own version -->
</dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-tiff</artifactId>
<version>3.3.1</version> <!-- Alternatively, build your own version -->
</dependency>
</dependencies>

如果要支持PGM格式,可以添加下面代码:

1
2
3
4
5
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-pnm</artifactId>
<version>3.3.1</version>
</dependency>

想要支持什么格式唯一要变的地方就是artifactId中内容,基本模版就是imageio-xxx

读取PGM图片

读取一张PGM图片,然后将图片的所有像素的像素值输出到一个文本文件,实现代码如下:

灰度图片

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
package Image;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.awt.image.Raster;
import java.io.*;

public class PGM {
public static void main(String[] args) throws IOException {
// Grayscale image
String path = "/Users/luowanqian/Downloads/Zach_Pillar_0001.pgm";
String output = "/Users/luowanqian/Downloads/output.txt";

BufferedImage image = ImageIO.read(new File(path));
Raster source = image.getRaster();
int width = image.getWidth();
int height = image.getHeight();

int[] pixels = new int[width*height];
source.getPixels(0, 0, width, height, pixels);

BufferedWriter out = new BufferedWriter(new FileWriter(output));
for (int i=0; i<width*height; i++)
out.write(pixels[i] + " ");
out.close();
}
}

Yale人脸数据库在人脸识别论文中频繁用到,因此做人脸识别这方面研究还是存一份这个数据库以备用。在网上可以很容易下载这个数据库,我在浙大蔡登教授的主页上下载了这个数据库(Four face databases in matlab format),主页上这个数据库有两种大小可以选择下载,一个是图像大小是32x32的数据库,另一个是64x64,本文选择的是32x32。

数据集说明

主页上有对这个数据集说明:

Contains 165 grayscale images in GIF format of 15 individuals. There are 11 images per subject, one per different facial expression or configuration: center-light, w/glasses, happy, left-light, w/no glasses, normal, right-light, sad, sleepy, surprised, and wink.

显示人脸图像

主页上提供显示数据库中人脸图像的代码,整理如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
% load data
load('Yale_32x32.mat');

faceW = 32;
faceH = 32;
numPerLine = 11;
ShowLine = 2;

Y = zeros(faceH*ShowLine, faceW*numPerLine);
for i=0:ShowLine-1
for j=0:numPerLine-1
Y(i*faceH+1:(i+1)*faceH, j*faceW+1:(j+1)*faceW) = reshape(fea(i*numPerLine+j+1,:), [faceH, faceW]);
end
end

imagesc(Y);
colormap(gray);

pause();

导出数据到文本文件

为了后续使用,我将人脸数据导出到文本文件中,文本每行包含了一张人脸数据以及该人脸对应的类标,写了一个简短的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
clear all; close all; clc;

% Load data
% Contains variables 'fea' and 'gnd'
% Each row of 'fea' is a face; 'gnd' is the label
load('Yale_32x32.mat');

output = 'data.txt';
[row, col] = size(fea);

file = fopen(output, 'w');
for i=1:row
for j=1:col
fprintf(file, '%d ', fea(i,j));
end
fprintf(file, '%d\n', gnd(i));
end

fclose(file);

代码运行后生成一个文本文件 __data.txt__。

关于 Hadoop Streaming 的使用,可以参考官方网站 Hadoop Streaming。个人觉得使用Steaming这个功能,可以使用其他语言来编写MapReduce程序,相比使用Java来编写程序,工作量小了很多。

根据《Hadoop权威指南》中例子使用python来实现 Max Temperature 这个程序,要分别实现Map函数以及Reduce函数。

Map函数

1
2
3
4
5
6
7
8
9
10
#!/usr/bin/env python

import re
import sys

for line in sys.stdin:
val = line.strip()
(year, temp, q) = (val[15:19], val[87:92], val[92:93])
if (temp != "+9999" and re.match("[01459]", q)):
print "%s\t%s" % (year, temp)

Reduce函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/usr/bin/env python

import sys

(last_key, max_val) = (None, -sys.maxint)
for line in sys.stdin:
(key, val) = line.strip().split("\t")
if last_key and last_key != key:
print "%s\t%s" % (last_key, max_val)
(last_key, max_val) = (key, int(val))
else:
(last_key, max_val) = (key, max(max_val, int(val)))

if last_key:
print "%s\t%s" % (last_key, max_val)

本文用到的文件如下:

其中 1901.txt为数据文件,max_temperature_map.py定义了Map函数,max_temperature_reduce.py定义了Reduce函数

定义好Map函数以及Reduce函数后,利用unix的管道将Map过程以及Reduce过程连接起来,使用下面命令

1
$ cat 1901.txt | ./max_temperature_map.py | sort | ./max_temperature_reduce.py

运行结果如下:

运行结果正确,此时就可以使用Hadoop的Streaming了,使用下面命令

1
2
3
4
5
6
$ hadoop jar $HADOOP_HOME/share/hadoop/tools/lib/hadoop-streaming-2.7.3.jar \
-fs file:/// \
-input 1901.txt \
-output output \
-mapper max_temperature_map.py \
-reducer max_temperature_reduce.py

注意:如果文件是在本地文件系统中而不是HDFS中时,要使用 -fs file:/// 这个命令选项

运行结果保存到 output 文件夹中

在实验《Hadoop权威指南 第3版》书本上的例子4-2时发现了一个关于 FileSystem 的问题。例子代码如下:

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
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.io.compress.CompressionCodec;
import org.apache.hadoop.io.compress.CompressionCodecFactory;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;

public class FileDecompressor {
public static void main(String[] args) throws Exception {
String uri = args[0];
Configuration conf = new Configuration();
FileSystem fs = FileSystem.get(URI.create(uri), conf);

Path inputPath = new Path(uri);
CompressionCodecFactory factory = new CompressionCodecFactory(conf);
CompressionCodec codec = factory.getCodec(inputPath);
if (codec == null) {
System.err.println("No codec found for " + uri);
System.exit(1);
}

String outputUri =
CompressionCodecFactory.removeSuffix(uri, codec.getDefaultExtension());

InputStream in = null;
OutputStream out = null;
try {
in = codec.createInputStream(fs.open(inputPath));
out = fs.create(new Path(outputUri));
IOUtils.copyBytes(in, out, conf);
} finally {
IOUtils.closeStream(in);
IOUtils.closeStream(out);
}
}
}

编译好按照使用书本上的命令行运行程序

1
% hadoop FileDecompressor file.gz

运行时抛出了 java.io.FileNotFoundException 异常

错误说找不到文件 __/user/luowanqian/file.gz__,但是我的 file.gz 并不在这个目录里面,而是在 __/home/luowanqian/Downloads/Temp/MaxTemperature__,如果使用绝对路径作为参数传递给程序也会报错,最终在绝对路径前面加上 file:// 后程序就没有报错了。

初步分析了这个问题,个人认为是程序默认在HDFS中寻找 __file.gz__,而 file.gz 是在本地文件系统中,因此程序要识别这个文件是在本地文件系统中,需要在路径前面添加 __file://__,为了进一步验证我的猜想,我修改了原来的代码。

1
FileSystem fs = FileSystem.get(URI.create(uri), conf);

修改成

1
FileSystem fs = FileSystem.getLocal(conf);

再次使用下面命令运行程序

1
% hadoop FileDecompressor file.gz

这个时候程序就正常运行了

至于程序如何判断文件是在怎么样的文件系统目前还不知道怎么解决,初学Hadoop还是很多地方不清楚。

最近在学习Hadoop时候遇到了一个问题,就是操作Hadoop文件系统时遇到 Connection Refused 问题,当时问题显示如下:

遇到这问题时,首先查看了错误信息提示中提到的网址 https://wiki.apache.org/hadoop/ConnectionRefused,文章中提示说是端口问题,但是经过排查不是端口问题,所以只能排查其他原因了。首先使用Java的 jps 命令查看JVM,查看结果如下:

发现没有NameNode,然后查看了Hadoop关于NameNode日志(日志文件默认在Hadoop安装目录中的 logs 目录下),发现了一些东西:

日志说 /tmp/hadoop-luowanqian/dfs/name 目录不存在,这个目录是HDFS文件系统的镜像文件保存的地方,HDFS文件系统格式化后其镜像文件默认保存在 tmp 目录中,而这个目录中的文件在重启系统后会自动被删除,因此访问HDFS文件系统会失败,重新格式化HDFS文件系统后再访问文件系统正常了。