-
为什么要做测试(必答题)
测试不能提高代码质量;
自己写的代码自己一般检查不出来BUG;
流程中复杂的数据结构,第三方模块和方法,内部细节不够详细(类似于黑盒),必须要测试才能发现潜在的bug;
bug可能因为两块正常的代码耦合在一起而产生,所以通畅地测试整个流程很有必要; -
web开发的测试包括哪些
宏观的测试内容包括:
用户感受:页面样式正确、文案通顺、交互正常、流程逻辑合理;
运营需求:流程(样式、文案、引导等等)和目的一致,数据能随时统计;
开发人员:代码重用率、需求扩展是否容易、文档是否齐全; -
测试需要遵循哪些规则
-
有哪些常规的测试方法
代码评审
黑盒测试 -
有哪些测试工具,自动化测试工具
Chrome DevTools
postman(模拟请求)
fiddler(抓包)
Selenium(模拟访问 & 结果存储分析) -
如何编写测试文档(范例)
用例:
描述:
前置条件:
后置条件:
预期结果:
是否通过:
附链接参考:http://www.ibm.com/support/knowledgecenter/zh/SSYMRC_4.0.0/com.ibm.rational.test.qm.doc/topics/t_testcase_template_ref.html -
在以往项目开发过程中,测试的时候遇到的问题(必答题)
调用api错误(调了别人的分支、调用了测试分支);
数据解析错误(传参不一样数据结构不一样的接口);
sql错误:表缺少字段,或者存储了一个null到非null字段;
分类: 三个月水平
spl_autoload_register 入门级讲解
标签(空格分隔): PHP自动加载
__autoload()
了解 spl_autoload_register() 前先了解一下 __autoload(),或者直接 跳过此节:
function __autoload($c)
{
var_dump($c);
}
new Say();
//输出
string(3) "Say"
Fatal error: Class 'Say' not found in E:\Dropbox\liubole\htdocs\test\index.php on line xx
当Say类不存在的时候,自动执行了 __autoload() 函数,并将类名当做参数传入了 __autoload()。
如果在 __autoload() 里面 include 一下包含 Say 类的文件,可以“亡羊补牢”,代码继续正常执行:
function __autoload($c)
{
echo "not fund class \"{$c}\", auto loading...\n";
//libs下的say.php,大小写无影响
include_once __DIR__ . "/libs/{$c}.php";
}
$say = new Say();
$say->hello();
包含 Say 类(libs/say.php)的文件内容:
class Say
{
function __construct()
{
echo "init class \"Say\"...\n";
}
function hello()
{
echo "say: hello~\n";
}
}
再次执行,结果如下:
not fund class "Say", auto loading...
init class "Say"...
say: hello~
至此我们大概了解了 __autoload() 的实际执行情况。
spl_autoload_register()
PS:前缀 SPL 的全称是 Standard PHP Library。
当PHP找不到类文件会调用 __autoload() 方法,当注册了自己的函数或方法时,PHP不会调用 __autoload() 函数,而会调用自定义的函数。
如果在你的程序中已经实现了__autoload函数,它必须显式注册到__autoload栈中。因为spl_autoload_register()函数会将Zend Engine中的__autoload函数取代为spl_autoload()或spl_autoload_call()。
上面的话来自 php.net。OK,那怎样算 显式注册到__autoload栈中 呢?
◔ ‸◔ ... ◔ ‸◔ ..... ◔ ‸◔ ...... ◔ ‸◔ ........◔ ‸◔
答案是:spl_autoload_register("__autoload") 。以下是我找来的一点摘要,描述了为何需要这样做:
-
autoload机制的主要执行过程为:
(1) 检查执行器全局变量函数指针autoload_func是否为NULL。
(2) 如果autoload_func==NULL,则查找系统中是否定义有__autoload()函数,如果没有,则报告错误并退出。
(3) 如果定义了__autoload()函数,则执行__autoload()尝试加载类,并返回加载结果。
(4) 如果autoload_func不为NULL,则直接执行autoload_func指针指向的函数用来加载类。注意此时并不检查__autoload()函数是否定义。 -
如果既实现了__autoload()函数,又实现了autoload_func(将autoload_func指向某一PHP函数),那么只执行autoload_func函数。
-
SPL autoload机制的实现是通过将函数指针autoload_func指向自己实现的具有自动装载功能的函数来实现的。SPL有两个不同的函数spl_autoload, spl_autoload_call,通过将autoload_func指向这两个不同的函数地址来实现不同的自动加载机制。spl_autoload的功能比较简单,而且它是在SPL扩展中实现的,我们无法扩充它的功能。所以我们需要spl_autoload_register将autoload_func指向spl_autoload_call的这个功能。
-
在SPL模块内部,有一个全局变量autoload_functions,它本质上是一个HashTable,不过我们可以将其简单的看作一个链表,链表中的每一个元素都是一个函数指针,指向一个具有自动加载类功能的函数。spl_autoload_call按顺序执行这个链表中每个函数,在每个函数执行完成后都判断一次需要的类是否已经加载,如果加载成功就直接返回,不再继续执行链表中的其它函数。
-
自动加载函数链表autoload_functions由spl_autoload_register函数维护,它可以将用户定义的自动加载函数注册到这个链表中,并将autoload_func函数指针指向spl_autoload_call函数。
欲知详情请移步 __autoload机制详解以及与spl_autoload_register的区别, 文章把原因讲的很详细,时间匆忙我仅囫囵吞枣瞄了眼。
本文涉及资料:
PHP: spl_autoload_register - Manual
__autoload机制详解以及与spl_autoload_register的区别
判断点是否在矩形内的算法
声明:想获取更多更详细的知识请移步:计算几何算法概览
需求描述:
有一个 4*4 的方格,用户可以选择起点和落点形成矩形,每次形成的矩形不能重叠,起点可以和落点坐标一样。现已知起点、未选点集合和已选点集合,求落点的集合。
算法重点:转换为判断点P是否在一个矩形内。
方法一:建立平面直角坐标系,根据横坐标和纵坐标判断点是否在矩形内。
/**
* 判断点是否在矩形内(含边)。p1、p2是矩形的两个对角点。
* 该算法和平面直角坐标系怎么建立无关
* @param $rc
* @param $p
* @return bool
*/
function isInRect($rc, $p)
{
$xi = ($p->x - $rc->p1->x) * ($p->x - $rc->p2->x);
$yi = ($p->y - $rc->p1->y) * ($p->y - $rc->p2->y);
return ($xi <= 0) && ($yi <= 0);
}
方法二:假设矩形左、右、上、下分别在直线 L1、L2、L3、L4 上,只需要证明点 P 同时在上下、左右直线之间即可。
过P作一条直线相交于 L1、L2 的 C1 、C2 ,向量PC1 、PC2 同向则P在 L1、L2 之外,反向则在 L1、L2 之内。同理可判断P是否在 L3、L4 之间。
$$ PC_1 = (C_1x - P_x , C_1y - P_y); PC_2 = (C_2x - P_x , C_2y - P_y);$$
$$ \frac{C_2x - P_x}{C_1x - P_x } > 0 或 \frac{C_2y - P_y}{C_1y - P_y } > 0 => PC_1、PC_2 同向 $$
$$(C_1x-P_x)^2 + (C_1y-P_y)^2 = 0 => PC_1为零向量 => PC_1、PC_2 同向$$
$$(C_2x-P_x)^2 + (C_2y-P_y)^2 = 0 => PC_2为零向量 => PC_1、PC_2 同向$$
如果:
$$ L_1: y = mx + t_1; $$
$$ L_2: y = mx + t_2; $$
$$ C_1C_2: y = (m+1)x; $$
$$ P_1:(P_1x, P_1y); P_2:(P_3x, P_3y) 是L_1上的两个矩形顶点;$$
$$ P_3:(P_3x, P_3y); P_4:(P_4x, P_4y) 是L_2上的两个矩形顶点;$$
则:
$$ 若点P和{P_1,P_2,P_3,P_4}中的某一点重合,则P在矩形内; $$
$$ 若 (t_2 - P_x)*(t_1 - P_x)>0, t_1=\frac{P_1yP_2x-P_1xP_2y}{P_2x-P_1x},t_2=\frac{P_3yP_4x-P_3xP_4y}{P_4x-P_3x} 则P在矩形内;$$
$$ 若 (t_2 - P_x)*(t_1 - P_x)>0, t_1=\frac{P_1yP_2x-P_1xP_2y}{P_2x-P_1x},t_2=\frac{P_3yP_4x-P_3xP_4y}{P_4x-P_3x} 则P在矩形内;$$
/**
* 此处省略n行代码
*/
方法三:以点P为起点,向左作射线,如果射线和矩形的焦点个数是奇数,则P在矩形内。
PS:对以下特例不做考虑:1、对于矩形的水平边不作考虑;2、对于矩形的顶点和L相交的情况。
/**
* 此处省略m行代码
*/
总结:方法一简单实用,方法三可以推广到判断点是否在任意多边形内。
RabbitMQ在项目中的使用方式+简略介绍
声明:文章写于2016-11-10,项目中RabbitMQ的使用方式在后面可能会发生变化。
消息队列(MessageQueue)的好处:
- 简化业务流程
- 异步处理消息
- 减少组件耦合
- 消减数据峰值
消息队列的应用场景:
- 场景1:
支付成功以后,给用户发送优惠券or红包; -
场景2:
用户注册成功后发送通知邮件or短信; -
场景3:
活动里面的站内信发送;
. . . . . .
总结起来就是,把单个业务简洁化,通过消息系统把不相关的业务连接起来,实现更复杂的业务链,降低整个系统的复杂度。
安装
安装方法自行google,如果要在生产环境中安装请先联系运维,涉及到安全问题。
项目中使用
//发布消息
use CI123\Shop\Tool\RabbitMq\Publisher;
Publisher::publish('routing_key', $message);
//监听消息
use Api\Tool\RabbitMq\Subscriber;
Subscriber::subscribe('routing_key', function ($msgObj) {
$message = $msgObj->body;
Subscriber::ack($msgObj);
});
以上是对RabbitMQ极其简陋的介绍,下面是重点:
一、我们项目中怎么设置RabbitMQ的:
1. 持久化
真正的持久保存消息,需要设置三个地方:
1.1 将交换机设置成durable;
1.2 将队列设置成durable;
1.3 将消息的delivery_mode设置成2;
PS:
缺少任何一个设置,都无法真正持久化;
持久化会消耗额外的性能;
2. 自动删除
2.1 消息在队列里存在时间是5分钟;
2.2 队列本身存在时间是30分钟;
2.3 过期的消息会被移出到“死信队列”;
2.4 死信队列里的消息存在7天;
2.5 死信队列本身存在10天;
3. 消费者应答机制(ack/nack机制)
3.1 如果消息已经被正确消费了,你可以通过ack方法告诉rabbitmq,rabbitmq会删除这条消息;
3.2 反之,你可以调用nack告诉RabbitMQ消费失败,rabbitmq会立马将消息移到“死信队列”;
3.3 或者什么都不做,等待消息过期后被移动到“死信队列”。
4. 生产者确认机制(Confirm机制)
4.1 发布消息后,发送者可以通过异步的回调知道消息是否发送成功;
PS:目前项目中基本没有使用。
二、初步理解RabbitMQ
1. 四个比较重要的概念
- 虚拟主机(vhost):每个vhost都有单独一套交换器、队列、绑定规则
- 交换器(exchange):将消息发送到队列
常见相关名词:
+ 名称(exchange):交换器名字,不能重复
+ 类型(type):fanout/direct/topic/headers
+ 持久化(durable):服务重启后是否要恢复
+ 自动删除(auto-delete):没有队列使用交换器时自动删除 - 队列(queue):存取消息
常见相关名词:
+ 名称(queue):队列名字,不能重复
+ 持久化(durable):服务重启后是否要恢复
+ 自动删除(auto-delete):没有消费者时自动删除
+ 声明(arguments): 消息存在时间(x-message-ttl)、队列存在时间(x-expires) - 绑定(binding):将队列和交换机绑定,以通过routing_key来收发消息;
2. 四种Exchange
- fanout :广播
- direct:routing_key全部匹配
- topic:可以理解为在direct的基础上给routing_key增加了正则匹配,当然肯定不是正则;
- headers:消息里包含headers,如果消息的headers和队列能匹配上就将消息投递到该headers,可以指定是完全匹配(all)还是只需要匹配单个(one);
3. 其他概念和结论
- 消息的获取有主动拉取、被动接收,发布/订阅模式是被动接收;
- 交换器、队列的属性一旦声明就不可更改,但是绑定是可以解绑的(unbind);
- 当一个 message 被路由到多个 queue 中时,在某个 queue 中的某个 message 的“死亡”不会对相同 message 在其他 queue 中的生存状况;
- 同时设置了队列生存时间T1和消息生存时间T2后,消息在队列中的存在时间是T1/T2之中的最小值;
- 如果没有设置死信队列,即便你设置了durable,消息到期后也会被丢弃;
4.目前的使用状况
- 按照普通的消息队列来使用:佣金、理财、保险;
- 改造为延时队列:微信模板消息,通过延时队列来重发上次发送失败的消息;
- 可以改造为任务队列使用:待定;
深入理解CI框架:框架如何运转起来(原理篇)
标签(空格分隔): codeigniter 源码
一、核心
index.php:入口文件
入口文件主要定义路径和环境,包括BASEPATH、FCPATH、APPPATH等常量表示的路径和application、system等变量表示的路径;
index.php通过$_SERVER['CI_ENV']来分辨当前的执行环境,目前支持development、testing、production三种(默认development),如果你在apache里配置CI_ENV为其他值(比如mytest),项目会输出错误信息并停止执行;
core/CodeIgniter.php:分派请求
CodeIgniter.php加载了各种各样的类和方法,它解析用户的请求(URI类),判断请求对应哪个控制器的哪个方法(Router类),创建控制器对象,执行方法,得到结果并输出(Output类),期间还会加载并执行预先设置好的各种钩子(Hooks类)。
- 很显然,
CodeIgniter.php是CI框架最重要的文件之一,它根据用户请求调用不同类处理并输出结果。- CI框架解析用户请求时,主要依据
$_SERVER这个变量($_SERVER['REQUEST_METHOD'])- 除了各种类,
CodeIgniter.php还加载了很多公共方法,详见core/Common.php和core/compat
二、提取核心代码
$RTR =& load_class('Router', 'core');
$class = ucfirst($RTR->class);
require_once BASEPATH.'core/Controller.php';
$CI = new $class();
三、细枝末节
core/Common.php:公共方法
Common Functions——这是Common.php定位,用来存放各种公共方法,比如is_php、is_https、is_cli、show_404、function_usable等等。
core/compat目录下也存放有很多公共方法文件,比如hash.php、mbstring.php、password.php、standard.php,我们经常用的array_column函数就是在standard.php里定义的。
四、黑科技
extract:从数组中将变量导入到当前的符号表
//index.php
$vars = array('name' => 'jane', 'age' => 20);
extract($vars);
var_dump($name);
ob_xxx函数:把输出放到缓冲区、从缓冲区获取内容
ob_start:打开输出控制缓冲
ob_end_flush:冲刷出(送出)输出缓冲区内容并关闭缓冲
ob_end_clean:清空(擦除)缓冲区并关闭输出缓冲
//index2.php
//将一个php文件保存到文本文件里
//load->view 如何实现的
ob_start();
include('./index.php');
$buffer = ob_get_contents();
file_put_contents('index.php.txt', $buffer);
@ob_end_clean();
ob_end_flush();
ReflectionMethod:反射类
//检查方法是否可以访问
//如何在访问一个private方法或不存在的方法的时候显示404
$reflection = new ReflectionMethod($class, $method);
if ( ! $reflection->isPublic() OR $reflection->isConstructor())
{
$e404 = TRUE;
}
网页返回事件监听
一、为什么要监听返回事件
个人中心改版后,用户进入个人中心,从个人中心进入我的资料,在我的资料里面可以修改昵称,头像,邮寄地址等信息。在修改完信息之后,会跳转到我的资料页面,代码中是直接指定了我的资料页面地址。于是,在修改完信息后的我的资料页面点返回按钮,会返回到修改信息的页面,而不会返回到个人中心。如下图历史记录。

二、监听返回事件方法
1、监听返回按钮的方法:
window.addEventListener("popstate", function(e) {
alert("我监听到了浏览器的返回按钮事件啦");//根据自己的需求实现自己的功能
}, false);
2、如果这样写,还是会返回到上一页,所以还需要使用pushstate增加一个本页的url。
function pushHistory() {
var state = {
title: "title",
url: "#"
};
window.history.pushState(state, "title", "#");
}
3、完整的写法:
pushHistory();
window.addEventListener("popstate", function(e) {
window.location.href="./wx_grzx.html";
}, false);
function pushHistory() {
var state = {
title: "title",
url: "#"
};
window.history.pushState(state, "title", "#");
}
在实践中,这个方法是会有bug的。如果修改昵称页面不点击保存按钮,直接返回的话会返回到我的资料后立即跳转到个人中心。
于是呢,就出现了使用定时器来解决的方法。
pushHistory();
var bool=false;
setTimeout(function(){
bool=true;
},1500);//1.5秒后将bool设置为true
window.addEventListener("popstate", function(e) {
if(bool) {
window.location.href="./wx_grzx.html";
} else {
bool = true;//如果在1.5秒内触发了一次,则直接将bool设置为true
}
pushHistory();
}, false);
function pushHistory() {
var state = {
title: "title",
url: "#"
};
window.history.pushState(state, "title", "#");
}
三、拓展知识
1、popstate用法:
popstate, 是HTML5新标准, 产生的目的就是做客户端前进后退。
每当处于激活状态的历史记录条目发生变化时,popstate事件就会被触发。如果当激活状态的历史记录条目是由history.pushState()方法创建的或者由history.replaceState()方法修改过的,则popstate事件的state属性包含了这条历史记录条目的state对象的一个拷贝。
需要注意的是调用pushState或者replaceState并不会触发popstate事件,只有点击浏览其回退按钮才回触发。
2、pushState和replaceState的异同。
相同:用法相同
以pushState来做说明:
格式:pushState(state, title, url)
参数说明:
state:是一个javaScript对象或者json字符串,它关系到由pushState()方法创建出来的新的history实体。用来存储插入到历史记录条目的相关信息。
title:一个暂时被某些浏览器忽略的参数,不过它在将来可能会被用上。现在的话可以传一个空字符串,也可以传一个简短的标题。
url:用来传递新的history实体的url。即显示在地址栏的url。
不同:pushState用于向history添加当前页面的记录,而replaceState是修改当前页面在history中的记录。
3、参考文档
http://blog.csdn.net/tianyitianyi1/article/details/7426606
四、现有程序中如何使用
现在程序中使用的是,修改信息后,不指定跳转路径,而是使用window.history.go(-1);,这样在修改了信息之后返回到我的资料,我的资料再返就是到个人中心了。
数据加密技术
目录:
数据加密技术有哪些分类
每个分类的实现方式和常见算法
正文:
一、数据加密技术有哪些分类
对称加密算法
非对称加密算法
单向加密(散列算法)
散列算法不是加密算法, 因为如果目的是加密,必须满足的一个条件是加密过后可以解密。但是散列算法是无法从结果还原出原始数据的。
二、每个分类的实现方式和常见算法
对称加密算法
1、定义:
对称加密算法是应用较早的加密算法,技术成熟。在对称加密算法中,数据发信方将明文(原始数据)和加密密钥一起经过特殊加密算法处理后,使其变成复杂的加密密文发送出去。收信方收到密文后,若想解读原文,则需要使用加密用过的密钥及相同算法的逆算法对密文进行解密,才能使其恢复成可读明文。在大部分对称加密算法中,使用的密钥只有一个,发收信双方都使用这个密钥对数据进行加密和解密,这就要求解密方事先必须知道加密密钥。
在PHP中也有封装好的对称加密函数:Urlencode/Urldecode base64_encode()/base64_decode()
严格的来说,这两个函数其实不算是加密,更像是一种格式的序列化。
2、常见的加密算法:
DES(Data Encryption Standard):数据加密标准,速度较快,适用于加密大量数据的场合。
3DES(Triple DES):是基于DES,对一块数据用三个不同的密钥进行三次加密,强度更高。
AES(Advanced Encryption Standard):高级加密标准,是下一代的加密算法标准,速度快,安全级别高;据说是要取代DES
3、DES算法:
对称加密算法中最经典的算法莫过于DES加密算法。DES加密采用的是分组加密的方法,使用56位密钥加密64位明文,最后产生64位密文。DES算法的基本流程如下图所示。

现在对图的整个流程做简要的分析。DES对64位的明文分组M进行操作,M经过一个初始置换IP置换成m0,将m0明文分成左半部分和右半部分m0=(L0,R0),各32位长。然后进行16轮完全相同的运算,这些运算称为函数f,在运算过程中,数据与密匙结合。经过16轮运算之后,可以看到第16轮运算,将右侧第15轮运算的结果(R15)作为左侧运算的最终结果(L16),而右侧最后的结果(R16)为左侧第15轮运算结果(L15)和函数f运算结果的异或运算所得。此后,再将左、右部分合在一起经过一个逆置换,输出密文。
实际加密过程要分成两个同时进行的过程,即加密过程和密钥生成过程,如下图所示。

如上图所示,在16轮循环的每一轮中,密匙位移位,然后再从密匙的64位中选出48位。通过一个扩展置换将数据的右半部分扩展成48位,并通过一个异或操作替代成新的32位数据,在将其置换一次。这四步运算构成了图1中的函数f。然后,通过另一个异或运算,函数f的输出与左半部分结合,其结果成为新的右半部分,原来的右半部分成为新的左半部分。该操作重复16次。
DES算法的解密过程和加密过程几乎完全相同,只是使用密钥的顺序相反。
4、优缺点
优点:算法公开、加密解密的速度比较快,适合数据比较长时的使用。
缺点:
1、加密方和解密方使用同一个密钥,安全性得不到保障。
2、密钥传输的过程不安全,且容易被破解,每对用户每次使用对称加密算法时,都需要使用其他人不知道的惟一钥匙,这会使得发收信双方所拥有的钥匙数量呈几何级数增长,密钥管理成为用户的负担。对称加密算法在分布式网络系统上使用较为困难,主要是因为密钥管理困难,使用成本较高。
非对称加密算法
1、定义:
与对称加密算法不同,非对称加密算法需要两个密钥:公开密钥(publickey)和私有密钥(privatekey)。公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。
2、常见的加密算法:
RSA:由 RSA 公司发明,是一个支持变长密钥的公共密钥算法,需要加密的文件块的长度也是可变的;
DSA(Digital Signature Algorithm):数字签名算法,是一种标准的 DSS(数字签名标准);
ECC(Elliptic Curves Cryptography):椭圆曲线密码编码学。
3、http://www.cnblogs.com/zfxJava/p/5295957.html理解公钥和私钥(划重点)
4、优缺点
优点:对称加密算法不能实现签名,因此签名只能非对称算法。比对称加密算法更加安全。
缺点:
1、由于非对称加密算法的运行速度比对称加密算法的速度慢很多,当我们需要加密大量的数据时,建议采用对称加密算法,提高加解密速度。
2、公钥传输的过程不安全,易被窃取和替换,由此产生了数字证书。
单向加密(散列算法)
1、定义:
属于摘要算法(哈希/散列算法),不是一种加密算法,作用是把任意长的输入字符串变化成固定长的输出串的一种函数。加密性强的散列一定是不可逆的,这就意味着通过散列结果,无法推出任何部分的原始信息。任何输入信息的变化,哪怕仅一位,都将导致散列结果的明显变化,这称之为雪崩效应。散列还应该是防冲突的,即找不出具有相同散列结果的两条信息。具有这些特性的散列结果就可以用于验证信息是否被修改。
2、常见的散列算法:
1、MD5(Message Digest Algorithm 5):是RSA数据安全公司开发的一种单向散列算法,非可逆,相同的明文产生相同的密文。
2、SHA(Secure Hash Algorithm):可以对任意长度的数据运算生成一个160位的数值;
3、MD5加密注意点:
md5()为单向加密,没有逆向解密算法,但是还是可以对一些常见的字符串通过收集,枚举,碰撞等方法破解;所以为了让其破解起来更麻烦一些,所以我们一般加一点盐值(salt)并双重MD5;
例: md5(md5($password).'ylcf')
ylcf就是盐值。
4、特点
雪崩效应、定长输出和不可逆。
01背包问题基础
编 写: zhangyue
时 间: 2016-12-27
说 明: 利用背包问题解决红包使用问题
一 01背包问题
01背包问题是指在一个容量大小固定为M的背包中,装入体积和价值不同的宝石,怎么选择才能在背包中得到最大的价值。
二 动态规划
动态规划是指
通过拆分问题,定义问题状态和状态之间的关系,使得问题能够以一种循环的方式去解决。
通常基于一个递推公式和一个或多个初始状态,当前子问题的解由上一个子问题的解推出。
有一类活动的过程,由于它的特殊性,可将过程分成若干个互相联系的阶段,在它的每一阶段都需要作出决策,从而使整个过程达到最 好的活动效果。当然,各个阶段决策的选取不是任意确定的,它依赖于当前面临的状态,又影响以后的发展,当各个阶段决策确定后,就组成一个决策序列,因而也就确定了整个过程的一条活动路线
状态转移方程:
d(N,M) = max(d(N-1,M), d(N-1, M-weight(i)) + value(i));
d(N,M) 有容量为M的背包,有N个宝石
d(N-1,M) 当前的宝石不放入背包,所能获取的最大价值
d(N-1, M-weight(i)) + value(i) 当前宝石放入背包,所能够获取的最大价值
三 代码
function getResult($bagItems, $bagSize) {
var result = array();
//生成状态数组
foreach ($i = 0; $i < $bagSize; $i ++) {
foreach ($j = 0; $j < count($bagItems); $j ++) {
if ($bagItems[$j]['weight'] > $i) {
//当前重量大于当前背包总重量
if (j != 0) {
$result[$j][$i] = $result[$j-1][$i];
} else {
//第一个元素 $j-1不存在,不存在前一个状态
$result[$j][$i] = 0;
}
} else {
//当前重量小于背包,可以放入背包
if ($j != 0 ) {
$tmp_value = $result[$j-1][$i - $bagItems[$j]['weight']] + $bagItems[$j]['weight'];
$result[$j][$i] = max($result[$j-1][$i], $tmp_value);
} else {
//第一个元素 $j-1不存在,不存在前一个状态
$result[$j][$i] = $bagItems[$j]['weight'];
}
}
}
}
//根据得到的状态得到所选的数组
$return = array();//存储放回的组合
$cur_size = $bagSize;//当前红包容量
for ($n = count($bagItems); $n >= 0 ; $n--) {
if ($cur_size == 0) {
break;
}
if ($n == 0 && $bagItems[$n]['weight'] < $cur_size) {
$return[] = $bagItems[$n]['id'];
break;
}
if ($bagItems[$n][$cur_size] - $bagItems[$n-1][$cur_size - $bagItems[$n]['weight']] == $bagItems[$n]['weight']) {
//判断当前的这个商品是不是在最优解里,如果不在则不放入
$return[] = $bagItems[$n]['id'];
$cur_size = $cur_size - $bagItems[$n]['weight'];
}
}
//得到结果
return $return;
}
四 递推表格
函数得到的递推表格和最终最优结果:
| 体积 | 价值 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 4 | 4 | 0 | 0 | 0 | 0 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 |
| 5 | 5 | 0 | 0 | 0 | 0 | 4 | 5 | 5 | 5 | 5 | 9 | 9 | 9 |
| 6 | 6 | 0 | 0 | 0 | 0 | 4 | 5 | 6 | 6 | 6 | 9 | 10 | 11 |
五 红包问题
使用红包时,只需要将价值和体积都类比于红包的金额即可,这样可以选出最后得到的红包使用组合。
存在问题:
1 红包会涉及小数的问题,红包现在只保留两位小数,最简单的方法是给红包*100,这样会增加递推数组的大小,增加空间复杂度
2 红包还存在过期时间,可考虑如何加上过期时间这一条件
3 由于红包价格和体积是同一个值,可考虑简化算法
linux定时任务的设置 crontab
一、基本使用
#分 时 日 月 周
* * * * * /home/blue/do/rsyncfile.sh
二、语法
crontab [-u username] [-l|-e|-r]
选项与参数:
-u :只有 root 才能进行这个任务,亦即帮其他使用者创建/移除 crontab 工作排程;
-e :编辑 crontab 的工作内容
-l :查阅 crontab 的工作内容 (备份)
-r :移除所有的 crontab 的工作内容,若仅要移除一项,请用 -e 去编辑
三、crontab的限制
/etc/cron.allow:将可以使用 crontab 的帐号写入其中,若不在这个文件内的使用者则不可使用 crontab;
/etc/cron.deny:将不可以使用 crontab 的帐号写入其中,若未记录到这个文件当中的使用者,就可以使用 crontab 。
以优先顺序来说, /etc/cron.allow 比 /etc/cron.deny 要优先, 而判断上面,这两个文件只选择一个来限制而已,因此,建议你只要保留一个即可, 免得影响自己在配置上面的判断!一般来说,系统默认是保留 /etc/cron.deny ,你可以将不想让他运行 crontab 的那个使用者写入 /etc/cron.deny 当中,一个帐号一行!
![]()
四、crontab的原理
crond服务的最低侦测限制是分钟,所以cron 会每分钟去读取一次 /etc/crontab 与 /var/spool/cron 里面的数据内容,因此,只要你编辑完 /etc/crontab 这个文件,并且将他储存之后,那么 cron 的配置就自动的会来运行了!
执行脚本的用户:/var/spool/cron/
执行记录:/var/log/cron
![]()
五、crontab 的格式
代表意义 分钟 小时 日期(天) 月份 周 命令
数字范围 0-59 0-23 1-31 1-12 0-7 执行的命令
周的数字为 0 或 7 时,都代表『星期天』的意思!
辅助字符
特殊字符 代表意义
*(星号) 任何时刻
,(逗号) 代表分隔时段
-(减号) 代表一段时间范围内
/n(斜线) 代表每隔n单位间隔
六、周与日月
周和日月不是 并 的关系,而是 或 的关系。
你可以分别以周或者是日月为单位作为循环,但你不可使用「几月几号且为星期几」的模式工作。
30 12 11 11 5 root echo "just test" <==这是错误的写法
本来你以为十一月十一号且为星期五才会进行这项工作,无奈的是,系统可能会判定每个星期五作一次,或每年的 11月 11 号分别进行。
测试:(12月8号为星期4)
25 18 08 12 4 (echo "just test1" >> /tmp/test.txt ) //执行一次
25 18 07 12 4 (echo "just test2" >> /tmp/test.txt ) //执行一次
25 18 08 12 5 (echo "just test3" >> /tmp/test.txt) //执行一次
七、& 后台执行命令
1. command & : 后台运行,你关掉终端会停止运行
2. nohup command & : 后台运行,你关掉终端也会继续运行
参考:http://www.cnblogs.com/lwm-1988/archive/2011/08/20/2147299.html
八、2>&1 含义
先看一个例子:
0 2 * * * echo "just test" >> /tmp/test.txt 2>&1 &
这句话的意思就是在后台执行这条命令,并将错误输出2重定向到标准输出1,然后将标准输出1全部放到/dev/null 文件,也就是清空。
注释:
数字的含义:
0表示 键盘输入
1表示 标准输出
2表示 错误输出
>>是追加内容
> 是覆盖原有内容
示例:
0 2 * * * sh /tmp/test.sh 1>/tmp/out.file &
0 2 * * * sh /tmp/test.sh 2>/tmp/out.file &
0 2 * * * sh /tmp/test.sh 2>/tmp/out.file 2>&1 &
1、将tesh.sh 命令输出重定向到out.file, 即输出内容不打印到屏幕上,而是输出到out.file文件中。
2、2>&1 是将错误输出重定向到标准输出。 然后将标准输入重定向到文件out.file。
3、&1 表示的是文件描述1,表示标准输出,如果这里少了&就成了数字1,就表示重定向到文件1。
4 、& :后台执行
测试:
ls 2>1 : 不会报没有2文件的错误,但会输出一个空的文件1;
ls xxx 2>1: 没有xxx这个文件的错误输出到了1中;
ls xxx 2>&1: 不会生成1这个文件了,不过错误跑到标准输出了;
ls xxx >out.txt 2>&1 == ls xxx 1>out.txt 2>&1: 因为重定向符号>默认是1,这句就把错误输出和标准输出都传到out.txt 文件中。
九、注意点:2>&1写在后面的原因
格式:command > file 2>&1 == command 1> file 2>&1
首先是command > file将标准输出重定向到file中, 2>&1 是标准错误拷贝了标准输出,也就是同样被重定向到file中,最终结果就是标准输出和错误都被重定向到file中。
如果改成: command 2>&1 >file
2>&1 标准错误拷贝了标准输出的行为,但此时标准输出还是在终端。>file 后输出才被重定向到file,但错误输出仍然保持在终端。
![]()
JSONP
JSONP是什么
跨域资源共享(Resources Domain Resources Sharing),客户端从不同的域名发送JSON响应时绕过浏览器限制
JSONP原理解析
利用 script标签 的 src属性 进行跨域请求,服务器响应函数调用传参。
2.1 静态方法创建
代码:
<?php
//echo "a = 1";
?>
//html代码
<script type="text/javascript" src="http://ly.yungou.ws/test/index"></script>
<script type="text/javascript">
console.log(a); // 1
</script>
跨域请求成功!
2.2、 动态方法创建
动态创建 script标签的方式,添加到头部,来获取变量参数
<?php
echo "var a = 1;";
?>
//通过 script语句动态创建 script标签进行请求
<script type="text/javascript">
var script = document.createElement('script');
script.src = 'http://ly.yungou.ws/test/index';
var head = document.getElementsByTagName('head')[0];
head.appendChild(script);
console.log( a );
</script>
注意:原来动态创建 script 的方式发送请求 是异步的,虽然请求成功了,但是在使用变量时,请求还没有完成,相当于还没有定义变量,然后就报错了。
解决办法:使用callback
<?php
echo 'callback(123)';
?>
<script type="text/javascript">
var script = document.createElement('script');
script.src = 'http://ly.yungou.ws/test/index';
var head = document.getElementsByTagName('head')[0];
head.appendChild(script);
function callback(data){
console.log( data ); //callback(123) 对 callback 调用 输出 123
}
</script>
那我们就可以用这种动态的方式 很轻松的 拿到后台数据了,只不过前台声明的和 后台 调用的 函数名 需要一样才行,如上面的 然而这样也不太好,每次改动,那都要前后台对接一下。
所以我们可以把回调函数名放在参数中传输。案例如下:
<?php
$callback = $_GET[ 'callback' ]; // get 通过 callback键 得到 函数名
$userInfo = array( 'username' => 'leiyuan', 'password' => '123456' );// 生成数据
$data = json_encode($userInfo);
echo "{$callback}({$data});";// hello( json_encode($arr) )
通过 script语句动态创建 script标签进行请求
var script = document.createElement('script');
script.src = 'http://ly.yungou.ws/test/callback?callback=hello';
var head = document.getElementsByTagName('head')[0];
head.appendChild(script);
function hello(data){
console.log(data);// Object {username: "leiyuan", password: "123456"}
}
</script>
2.3 jsonp原理:
JSONP是一个协议(并且是非正式传输协议)。 该协议的一个要点就是允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了.
由于同源策略的限制,XmlHttpRequest只允许请求当前源(域名、协议、端口)的资源,为了实现跨域请求,可以通过script标签实现跨域请求,然后在服务端输出JSON数据并执行回调函数,从而解决了跨域的数据请求,
2.4 jsonp原理
首先在客户端注册一个callback, 然后把callback的名字传给服务器。
服务器先生成 json 数据。 然后以 javascript 语法的方式,生成一个function , function 名字就是传递上来的参数 jsonp. 最后将 json 数据直接以入参的方式,放置到 function 中,这样就生成了一段 js 语法的文档,返回给客户端。
客户端浏览器,解析script标签,并执行返回的 javascript 文档,此时数据作为参数,传入到了客户端预先定义好的 callback 函数里.(动态执行回调函数)
JSONP和JSON的区别:
目前为止最被推崇或者说首选的方案还是用JSON来传数据,靠JSONP来跨域。
JSON(JavaScript Object Notation)和JSONP(JSON with Padding)
JSON是一种数据交换格式,而JSONP是一种依靠开发人员的聪明才智创造出的一种非官方跨域数据交互协议。一个是描述信息的格式,一个是信息传递双方约定的方法。
1、ajax和jsonp这两种技术在调用方式上“看起来”很像,目的也一样,都是请求一个url,然后把服务器返回的数据进行处理,因此jquery等框架都把jsonp作为ajax的一种形式进行了封装;
2、但ajax和jsonp其实本质上是不同的东西。ajax的核心是通过XmlHttpRequest获取非本页内容,而jsonp的核心则是动态添加
```
JSONP使用注意事项
1、跨系统跨域名,获取基本信息,
ps:后端不能存在任何页面跳转登陆信息,否则会请求失败
2、jsonp的缺陷:不提供错误处理(对方拒绝请求,网络不通,请求地址或者参数不正确等等)如果动态插入的代码正常运行,你可以得到返回,但是如果失败了,那么什么都不会发生。
3、动态请求,都是异步请求。不支持同步请求,async无效。
请求不是通过XHR完成的,而是动态创建script标记,并请求url。跨域请求和dataType: "jsonp" 本身不支持同步操作。
4、若要在fun中使用jsonp请求后的参数,需要注意的是执行顺序,fun需要写在callback中才能成功调用参数。
5、如果没有定义jsonp和jsonpCallback,jsonp默认为"callback",jsonpCallback会是Jquery自动生成的函数名。
6、请求返回的是script tag,首先会调用jsonpCallback函数,不管是否找到该函数,都会调用success函数
参考资料
http://api.jquery.com/jQuery.ajax/