标签:算法

浏览模式: 标准 | 列表 | 图文
转:经典算法

转:经典算法

在微博上突然看到一篇贴子:
【#SAE技术分享#】《经典排序算法集锦》大多数排序算法都给出了每一步的状态,以方便初学者更容易理解,通俗易懂,部分难以理解的排序算法则给出了大量的图示,也算是一个特色吧~http://t.cn/SySUjx

附了一张图:
7295467ef304c55f6c7cb724fbea4bda
果然非常有用,因此,我也就转载了一下。。。原文地址我就不贴了,不过我暂时先将它的目录贴出来吧

集体智慧编程笔记(一):相似度算法

本文内容全部来自《集体智慧编程》一书,原书采用的是python,因为没有python编程环境,所以用PHP实现

<?php
//filename:test_collecting_preferences
//数据和代码来自《集体智慧编程》
//原文采用python实现,尝试用PHP进行转换
//@description 搜集用户偏好寻找相近用户
$datalist = array(
'Lisa Rose' => array(
'Lady in the Water' => 2.5,
'Snake on a Plane' => 3.5,
'Just My Luck' => 3.0,
'Superman Returns' => 3.5,
'You, Me and Dupree' => 2.5,
'The Night Listener'=> 3.0
),
'Gene Seymour' => array(
'Lady in the Water' => 3.0,
'Snake on a Plane' => 3.5,
'Just My Luck' => 1.5,
'Superman Returns' => 5.0,
'You, Me and Dupree' => 3.5,
'The Night Listener'=> 3.0
),
'Michael Phillips' => array(
'Lady in the Water' => 2.5,
'Snake on a Plane' => 3.0,
'Superman Returns' => 3.5,
'The Night Listener'=> 4.0
),
'Claudia Puig' => array(
'Snake on a Plane' => 3.5,
'Just My Luck' =>3.0,
'Superman Returns' => 4.0,
'You, Me and Dupree' => 2.5,
'The Night Listener'=>4.5
),
'Mick LaSalle' => array(
'Lady in the Water' => 3.0,
'Snake on a Plane' => 4.0,
'Just My Luck' => 2.0,
'Superman Returns' => 3.0,
'You, Me and Dupree' => 2.0,
'The Night Listener'=> 3.0
),
'Jack Matthews' => array(
'Lady in the Water' => 3.0,
'Snake on a Plane' => 4.0,
'Superman Returns' => 5.0,
'You, Me and Dupree' => 3.5,
'The Night Listener'=> 3.0
),
'Toby' => array(
'Snake on a Plane' => 4.5,
'Superman Returns' => 4.0,
'You, Me and Dupree' => 1.0,
),
);
//欧几里德距离
//它以经过人们的一致评价的物品为坐标轴,然后将参与评价的人绘制到图上,并考查他们彼此间的距离远近。
//偏好越相似的人,距离越近。不过我们还需要一个函数来对偏好越相近的情况给出越大的值,
//为此我们可以将函数值加1(这样可以避免遇到被零整除的错误),并取其倒数
//公式是 1 / (1 + sqrt ( pow( data[a][1] - data[b][1] .... ) ))
function sim_distance ( $datalist , $person1 , $person2)
{
$si = array();
foreach ( $datalist[$person1] as $moviename => $grade ){
if( array_key_exists( $moviename, $datalist[$person2] )){
$si[$moviename] = 1;
}
}
if( emptyempty( $si )){
return 0;
}
$powers = 0;
foreach ( $si as $moviename=>$val ){
$powers += pow( ($datalist[$person1][$moviename] - $datalist[$person2][$moviename] ), 2 );//两者影评分数相减的平方值
}
return 1 / (1+ sqrt($powers));
}
//测试 'Lisa Rose' 和 'Gene Seymour' 的相似度评价
//原书上求出来是 0.29429805508554946 , PHP 的结果是 0.29429805508555,默认精度没有python高
echo( sim_distance( $datalist , 'Lisa Rose' , 'Gene Seymour') );
echo( '<br/>' );

//皮尔逊相关系数
//该相关系统是判断两组数据与某一直线拟合程序的一种度量。对应的公司比欧几里德距离评价的计算公式要复杂
//但是它在数据不是很规范时(如影评者对影片的评价总是相对于平均水平偏离很大),会倾向于给出更好的结果
//皮尔逊相关度评价法首先会找出两位评论者都曾评过的物品
//计算两者的评分总和与平方和,并求得评分的乘积之和,最后,利用这个结果计算出相关系数
function sim_person ( $datalist ,$person1 , $person2)
{
$si = array();
foreach ( $datalist[$person1] as $moviename => $grade ){
if( array_key_exists( $moviename, $datalist[$person2] )){
$si[$moviename] = 1;
}
}
if( emptyempty( $si )){
return 1;
}
$n = count( $si );
$sum1 = $sum1Sq = $sum2 = $sum2Sq = $pSum = 0;
foreach ( $si as $moviename => $val ){
$sum1 += $datalist[$person1][$moviename]; //个人影评分数累加
$sum1Sq += pow( $datalist[$person1][$moviename], 2 );//个人影评分数平方的累加
$sum2 += $datalist[$person2][$moviename];
$sum2Sq += pow( $datalist[$person2][$moviename], 2 );
$pSum += ( $datalist[$person1][$moviename] * $datalist[$person2][$moviename]);//两人影评之乘积
}

$num = $pSum - ( $sum1 * $sum2 / $n); // 正常情况下,我怎么都觉得这是1吧?
$den = sqrt( ( $sum1Sq - pow( $sum1, 2 ) / $n) * ( $sum2Sq - pow( $sum2, 2 ) / $n) );
if ( $den == 0 ){
return 0;
}
return ($num / $den );
}
//继续测试 'Lisa Rose' 和 'Gene Seymour' 的相似度评价
//原书上求出来是 0.396059017191 , PHP 的结果是 0.39605901719067,这回。。。位数超过了python
echo( sim_person( $datalist , 'Lisa Rose' , 'Gene Seymour') );

?>

有点长,随便看看吧

Kmeans算法

Kmeans算法

做电子商务的不可避免的都会遇到价格区间的问题。这,主要显示在搜索的时候,如果你区间设的过大,那几乎把所有产品都列出来了。那和没有分区间没啥分别。因此,还真有一种算法可以解决这个问题(当然也只是基本解决,看上去不是特别的乱而己)。

这是在老王的博客上看到的:

means算法

Web开发中,CRUD做多了难免厌烦,其实还有很多细节可以挖掘,比如很多电子商务网站上都有商品价格区间,都是诸如1000-2000,2000-3000之类定死的,而没有按商品自己的分布规律来划分,此时有一种名为Kmeans的算法可以使用,效果很好,网上有很多现成的代码可以参阅,比如PHP的版本:

kmeanspp
K-Means Clustering in PHP

--EOF--

到百科看了一下,这么解释K-means:k-means 算法接受输入量 k ;然后将n个数据对象划分为 k个聚类以便使得所获得的聚类满足:同一聚类中的对象相似度较高;而不同聚类中的对象相似度较小。聚类相似度是利用各聚类中对象的均值所获得一个“中心对象”(引力中心)来进行计算的。

虽然这样能够处理,老王也给了一个PHP的解决方案,但在实际中,应用的范围并能算是特别大,有时候还是直接手写的。以前的时候,我们是在分类里,直接把几个区间定义好,然后在搜索的时候指定某一分类时,自动调用这个区间。理由是,如果你的产品分的很散,从几块到几千块的都有,这种分类区间,就只能定义到分类上了。

不过上面的算法可以学习一下

MYSQL官方的文章:几种无限分类的算法……

MYSQL官方的文章:几种无限分类的算法……

我只贴一种,其余的去看:http://dev.mysql.com/tech-resources/articles/hierarchical-data.html

The Adjacency List Model

Typically the example categories shown above will be stored in a table like the following (I'm including full CREATE and INSERT statements so you can follow along):

CREATE TABLE category(
category_id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(20) NOT NULL,
parent INT DEFAULT NULL);


INSERT INTO category
VALUES(1,'ELECTRONICS',NULL),(2,'TELEVISIONS',1),(3,'TUBE',2),
(4,'LCD',2),(5,'PLASMA',2),(6,'PORTABLE ELECTRONICS',1),
(7,'MP3 PLAYERS',6),(8,'FLASH',7),
(9,'CD PLAYERS',6),(10,'2 WAY RADIOS',6);

SELECT * FROM category ORDER BY category_id;

+-------------+----------------------+--------+
| category_id | name | parent |
+-------------+----------------------+--------+
| 1 | ELECTRONICS | NULL |
| 2 | TELEVISIONS | 1 |
| 3 | TUBE | 2 |
| 4 | LCD | 2 |
| 5 | PLASMA | 2 |
| 6 | PORTABLE ELECTRONICS | 1 |
| 7 | MP3 PLAYERS | 6 |
| 8 | FLASH | 7 |
| 9 | CD PLAYERS | 6 |
| 10 | 2 WAY RADIOS | 6 |
+-------------+----------------------+--------+
10 rows in set (0.00 sec)

In the adjacency list model, each item in the table contains a pointer to its parent. The topmost element, in this case electronics, has a NULL value for its parent. The adjacency list model has the advantage of being quite simple, it is easy to see that FLASH is a child of mp3 players, which is a child of portable electronics, which is a child of electronics. While the adjacency list model can be dealt with fairly easily in client-side code, working with the model can be more problematic in pure SQL.

Retrieving a Full Tree

The first common task when dealing with hierarchical data is the display of the entire tree, usually with some form of indentation. The most common way of doing this is in pure SQL is through the use of a self-join:

SELECT t1.name AS lev1, t2.name as lev2, t3.name as lev3, t4.name as lev4
FROM category AS t1
LEFT JOIN category AS t2 ON t2.parent = t1.category_id
LEFT JOIN category AS t3 ON t3.parent = t2.category_id
LEFT JOIN category AS t4 ON t4.parent = t3.category_id
WHERE t1.name = 'ELECTRONICS';

+-------------+----------------------+--------------+-------+
| lev1 | lev2 | lev3 | lev4 |
+-------------+----------------------+--------------+-------+
| ELECTRONICS | TELEVISIONS | TUBE | NULL |
| ELECTRONICS | TELEVISIONS | LCD | NULL |
| ELECTRONICS | TELEVISIONS | PLASMA | NULL |
| ELECTRONICS | PORTABLE ELECTRONICS | MP3 PLAYERS | FLASH |
| ELECTRONICS | PORTABLE ELECTRONICS | CD PLAYERS | NULL |
| ELECTRONICS | PORTABLE ELECTRONICS | 2 WAY RADIOS | NULL |
+-------------+----------------------+--------------+-------+
6 rows in set (0.00 sec)

Base64转换:AQAB=65537,你知道为什么吗?[转]

base64 encoder

原文来自博客园:http://www.cnblogs.com/midea0978/archive/2007/05/22/755826.html

作者为:在路上

先贴上BASE64的算法介绍:http://popscanner.icpcn.com/download/base64.doc,这是DOC文档,可以下载一看。

什么是Base64?

按照RFC2045的定义,Base64被定义为:Base64内容传送编码被设计用来把任意序列 的8位字节描述为一种不易被人直接识别的形式。(The Base64 Content-Transfer-Encoding is designed to represent arbitrary sequences of octets in a form that need not be humanly readable.)

为什么要使用Base64?

在设计这个编码的时候,我想设计人员最主要考虑了3个问题:
1.是否加密?
2.加密算法复杂程度和效率
3.如何处理传输?

加密是肯定的,但是加密的目的不是让用户发送非常安全的Email。这种加密方式主要就是“防君子不防小人”。即达到一眼望去完全看不出内容即可。
基 于这个目的加密算法的复杂程度和效率也就不能太大和太低。和上一个理由类似,MIME协议等用于发送Email的协议解决的是如何收发Email,而并不 是如何安全的收发Email。因此算法的复杂程度要小,效率要高,否则因为发送Email而大量占用资源,路就有点走歪了。

但 是,如果是基于以上两点,那么我们使用最简单的恺撒法即可,为什么Base64看起来要比恺撒法复杂呢?这是因为在Email的传送过程中,由于历史原 因,Email只被允许传送ASCII字符,即一个8位字节的低7位。因此,如果您发送了一封带有非ASCII字符(即字节的最高位是1)的Email通 过有“历史问题”的网关时就可能会出现问题。网关可能会把最高位置为0!很明显,问题就这样产生了!因此,为了能够正常的传送Email,这个问题就必须 考虑!所以,单单靠改变字母的位置的恺撒之类的方案也就不行了。关于这一点可以参考RFC2046。
基于以上的一些主要原因产生了Base64编码。


在路上作者的原文为:

在RSA加密算法中,RSA公钥的public exponent通常都是65537,用base64来表示就是AQAB,这个转换是怎么得来的呢?
通常的base64算法是实现byte[]与base64字符串之间的转换,如果你习惯下面的转换方式,那就错了:

Console.WriteLine(Convert.ToBase64String(Encoding.Default.GetBytes("65537")));

输出的结果:NjU1Mzc=,并不是AQAB。
那么对于数字转换为base64应该怎么转换呢?首先来回顾一下base64算法原理:

1、算法原理
Base64编码要求把3个8位字节(3*8=24)转化为4个6位的字节(4*6=24),之后在6位的前面补两个0,形成8位一个字节的形式。
具体转化形式如下图:

字符串 "张3"
byte[] 110101 011100 010100 110011
添加前缀00, byte[] 00110101 00011100 00010100 00110011
十进制 53 28 20 51
转码 1 c U z

可以这么考虑:把8位的字节连成一串110101011100010100110011
然后每次顺序选6个出来之后再把这6二进制数前面再添加两个0,就成了一个新的字节。之后再选出6个来,再添加0,依此类推,直到24个二进制数全部被选完。

那么,根据上面的对照表计算出字符串“张3”的base64编码就是"1cUz"
2、转码对照表

Value Encoding Value Encoding Value Encoding Value Encoding
0 A 17 R 34 i 51 z
1 B 18 S 35 j 52 0
2 C 19 T 36 k 53 1
3 D 20 U 37 l 54 2
4 E 21 V 38 m 55 3
5 F 22 W 39 n 56 4
6 G 23 X 40 o 57 5
7 H 24 Y 41 p 58 6
8 I 25 Z 42 q 59 7
9 J 26 a 43 r 60 8
10 K 27 b 44 s 61 9
11 L 28 c 45 t 62 +
12 M 29 d 46 u 63 /
13 N 30 e 47 v
14 O 31 f 48 w (pad) =
15 P 32 g 49 x
16 Q 33 h 50 y

3、AQAB与65537
根据上面的原理,实际上65537作为数字就不能直接转换字符串再转换为base64,这样只会得出错误的结果,应该直接将65537转换为2进制
Step 1: 1 0000 0000 0000 0001
由于转换需要3个8字节的数据,需要在左边加上前导0,结果就是
Step 2:0000 0001 0000 0000 0000 0001
然后依次取6位,加上00,变成下面的一窜数据
Step 3:0000 0000 0001 0000 0000 0000 0000 0001
每8转换为10进制
Step 4:0 16 0 1
对照转码表
Step 5:AQAB
这就是为什么AQAB=65537,按照上面的原理我们就可以将RSA加密的密钥大质数转换为byte[]或者base64字符串存储了,.net中就是按照base64格式存储的。
4、代码实现
如何用代码来实现上面的转换呢,在.net中有一个类BitConverter可以实现int32=>byte[]的转换,但是转换后高低位的存储位置颠倒了,需要转换过来
例如数字65520=1111 1111 1111 0000=〉BitConverter.GetBytes=〉byte[]={240,255,0,0}
实际我们需要转换为byte[]={0,0,255,240},接下来用Convert.ToBase64String就可以了,下面是演示代码[代码不具有通用性转换性,仅演示]:

public static void demo(){
byte[] bits=BitConverter.GetBytes(65537);
byte[] newbits=new byte[bits.Length-1]; //避免产生padding位,去掉高位0,仅取3位
for(int i=0;i<newbits.Length;i++){
newbits[i]=bits[bits.Length-2-i];//newbits[0]=bits[2],依次
}
String s=Convert.ToBase64String(newbits);
Console.WriteLine("Base64="+s);
newbits=Convert.FromBase64String(s);
for(int i=0;i<newbits.Length;i++){
bits[bits.Length-2-i]=newbits[i];//newbits[0]=bits[2],依次
}
bits[3]=0;//高位补0
Console.WriteLine("Num="+BitConverter.ToInt32(bits,0));

}

后来看到有人回复;

字节顺序取决于特定的计算机结构,所以不应该假定程序总运行在little-endian字节顺序的环境中:
byte[] bits=BitConverter.GetBytes((int)65537);
if(BitConverter.IsLittleEndian) Array.Reverse(bits);
int ofs = 0;
while(bits[ofs]==0 && ofs < bits.Length) ofs++;
String s=Convert.ToBase64String(bits,ofs,bits.Length - ofs);
//Write s
byte[] intBytes = new byte[4];
bits = Convert.FromBase64String(s);
bits.CopyTo(intBytes,intBytes.Length-bits.Length);
if(BitConverter.IsLittleEndian) Array.Reverse(intBytes);
int num = BitConverter.ToInt32(intBytes,0);
//Write num