shenshun


  • 首页

  • 归档

  • 标签

Random number part1

发表于 2017-10-31   |   分类于 日志

如何利用一个小范围随机数生成一个大范围的随机数

用一个5以内的随机数函数生成一个7以内的随机数函数

1
var rand5 = () => ~~(Math.random()*5) // 利用这个函数生成5以内的随机数

这题的解题思路很简单, 只要利用这个函数生成一个平均分布的范围大于7的随机数就可以了

1
2
3
4
5
6
7
8
9
10
var rand7 = function() {
var val = rand5()*5 + rand5() // 平均分布 6~30 所以标准范围是6~26
return val>26?rand7():val%7+1 // 27~30之间的数据全部抛弃重新随机
}
var b = [0,0,0,0,0,0,0,0,]
for(var i=0; i<100000000;i++) {
b[rand7()]++
// [0, 14286297, 14287880, 14282580, 14287830, 14278935, 14285595, 14290883]
// 从数据看出是随机的
}

延伸: 如何利用一个小范围随机数生成一个大范围的随机数

重点在于如何保证随机数的平均分布
从rand5()*5 + rand5() 可以看出实现的原理是先生成一个[5,10,15,20,25]间隔为5的随机数组后再使用一个随机数去填充这个数组, 那么对于更大范围的数比如一个大于25的数, 就需要rang5()*5*5+rand5()*5+rand5() 得到一个区间是 31~155 总的范围长度为125的数组
所以只需要递归获得一个大于范围的数组长度, 就可以得到一个由小范围随机数生成的大范围随机数, 具体代码就不贴出来了, 有兴趣可以自己实现一下

1
2
3
4
5
6
7
8
9
10
// 这里给出一个我偶然想到的阶乘
const factorial = function(n, x, num=n) {
// n 是对应的进制
// x 表示进位的长度
// num 表示被用于做乘法的数
// 所以 factorial(n, x, num) = num * n^x
const val = num.toString(n)
return x?parseInt(val.padEnd(val.length+x,"0"),n):1
}
console.log(factorial(2, 4, 5) === 5<<4) // true

安装nginx服务器

发表于 2017-06-14

基础介绍

Nginx是一款面向性能设计的HTTP服务器,相较于Apache、lighttpd具有占有内存少,稳定性高等优势。与旧版本(<=2.2)的Apache不同,nginx不采用每客户机一线程的设计模型,而是充分使用异步逻辑,削减了上下文调度开销,所以并发服务能力更强。整体采用模块化设计,有丰富的模块库和第三方模块库,配置灵活。 在Linux操作系统下,nginx使用epoll事件模型,得益于此,nginx在Linux操作系统下效率相当高。同时Nginx在OpenBSD或FreeBSD操作系统上采用类似于epoll的高效事件模型kqueue。

Nginx在官方测试的结果中,能够支持五万个平行连接,而在实际的运作中,可以支持二万至四万个平行链接。

安装nginx

本文中使用的是centos7 linux操作系统, 相关命令可能与centos5有所不同

直接安装

在centos7下可以通过yum命令直接安装编译过的nginx服务器

1
2
yum install nginx -y #安装最新版本的nginx
systemctl start nginx #centos7替代service命令

编译安装

在nginx服务器中, 模块化设计是其一大特色, 在1.9.11之前的版本中, 我们都需要通过编译来添加和删除模块, 所以编译安装也是一个通点

安装环境: yum

先安装准备环境

1
yum install gcc gcc-c++ automake pcre pcre-devel zlip zlib-devel openssl openssl-devel

gcc为GNU Compiler Collection的缩写,可以编译C和C++源代码等,它是GNU开发的C和C++以及其他很多种语言 的编译器(最早的时候只能编译C,后来很快进化成一个编译多种语言的集合,如Fortran、Pascal、Objective-C、Java、Ada、 Go等。)gcc 在编译C++源代码的阶段,只能编译 C++ 源文件,而不能自动和 C++ 程序使用的库链接(编译过程分为编译、链接两个阶段,注意不要和可执行文件这个概念搞混,相对可执行文件来说有三个重要的概念:编译(compile)、链接(link)、加载(load)。源程序文件被编译成目标文件,多个目标文件连同库被链接成一个最终的可执行文件,可执行文件被加载到内存中运行)。因此,通常使用 g++ 命令来完成 C++ 程序的编译和连接,该程序会自动调用 gcc 实现编译。
gcc-c++也能编译C源代码,只不过把会把它当成C++源代码,后缀为.c的,gcc把它当作是C程序,而g++当作是c++程序;后缀为.cpp的,两者都会认为是c++程序,注意,虽然c++是c的超集,但是两者对语法的要求是有区别的。
automake是一个从Makefile.am文件自动生成Makefile.in的工具。为了生成Makefile.in,automake还需用到perl,由于automake创建的发布完全遵循GNU标准,所以在创建中不需要perl。libtool是一款方便生成各种程序库的工具。
pcre pcre-devel:在Nginx编译需要 PCRE(Perl Compatible Regular Expression),因为Nginx 的Rewrite模块和HTTP 核心模块会使用到PCRE正则表达式语法。
zlip zlib-devel:nginx启用压缩功能的时候,需要此模块的支持。
openssl openssl-devel:开启SSL的时候需要此模块的支持。

下载相关安装包: wget

官方网址在http://nginx.org
你可以在这个页面查看当前仍旧支持的所有版本, 戳这里
在linux需要的目录下使用wget命令安装, 我的位置是/root/

1
2
3
wget http://nginx.org/download/nginx-1.13.1.tar.gz #下载对应文件
tar xvf nginx-1.13.1.tar.gz #x释放v报告相关信息f使用文件
cd nginx-1.13.1

编译nginx: ./configure

首先需要使用./configure相关命令

1
./configure --prefix=/usr/local/nginx --sbin-path=/usr/local/nginx/sbin/nginx --conf-path=/usr/local/nginx/conf/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx/nginx.pid --lock-path=/var/local/nginx.lock --usr=nginx --group=nginx --http-client-body-temp-path=/var/tmp/nginx/client/ --http-proxy-temp-path=/var/tmp/nginx/proxy/ --http-fastcgi-temp-path=/var/tmp/nginx/fcgi/ --http-uwsgi-temp-path=/var/tmp/nginx/uwsgi --http-scgi-temp-path=/var/tmp/nginx/scgi --with-pcre

以上命令分别设置安装路径, 运行文件路径, 配置文件路径, 报告错误路径, 访问路径等等, 部分内容可以在对应的主配置文件nginx.conf中修改。
configure

生成文件: make

编译生成nginx, 此时使用make命令, 会根据上一步生成的Makefile生成对应的模块

1
make

运行结果大致如下:
make

覆盖安装: make install

创建相应目录, 如果已有目录, nginx主文件会覆盖之前的主文件

配置文件 nginx.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
user  nobody; #启动用户
worker_processes 1; #启动进程数量
events { #设置网络连接
worker_connections 1024; #设置最大并发数
}
http { #http模块, 每个http模块包含多个server块, 用于监听不同的域名和端口请求,每个server块包含多个location块, server块可以配置文件引入、MIME-Type定义、日志自定义、是否启用sendfile、连接超时时间和单个链接的请求上限等
include mime.types; #文件扩展名与文件类型映射表
default_type application/octet-stream; #默认文件类型
sendfile on; #是否调用 sendfile 函数来输出文件, 避免用户缓冲区和内核缓冲区之间的拷贝
keepalive_timeout 65; #超时时长
#gzip on; #gzip压缩
server { #设置一个虚拟主机, 监听端口
listen 80; #监听80端口
server_name localhost; #当前虚拟主机的名称
location / { #拦截 / 目录下所有的请求
root html; #root是访问文件的真实路径, 可以是绝对路径或相对路径
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html; #服务器50x错误
location = /50x.html {
root html;
}
}
}

模板替换及正则捕捉

发表于 2017-06-03   |   分类于 日志

最近在做关于模板替换的一些练习, 总结了一些经验。
平时当我们需要创建一个HTML元素的时候, 总是需要借助相关的api

1
2
const node = document.createElement('div')
node.className = data

这样显得很繁琐, 累赘, 如果偷懒点也许会写成这样

1
const node = `<div class="${data}"></div>`

但这是ES2015的新语法嘛, 所以我们可以尝试自己制作一个简单的模板替换

1
const node = `<div class="{{data}}"></div>`

如上所示, 我们需要用正则匹配相关变量名, 需要用到string.prototype.replace() 这个api

1
2
3
4
5
6
7
const template = function(tem, data) {
return tem.replace(/{{(\w+)}}/g, function(str, p1) {
if (!p1)
return ""
return data[p1]
})
}

需要了解的是, string.prototype.replace()这个api的第一个参数是需要匹配的正则或是单独的字符串, 第二个参数可以是新的字符串, 也可以是一个function

1
2
3
4
5
6
7
function replacer(match, p1, p2, p3, offset, string) {
// match 代表匹配的子串
// p1,p2, ... 代表被()正则符号捕捉的字符串
// offset 代表匹配子串在原字符串的偏移量
// string 原字符串
return "返回新字符串";
}

如果需要支持对象, 需要对原函数进行改造

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const template = function(tem, data) {
return tem.replace(/{{(\w+(?:\.\w+)*)}}/g, function(match, p1) {
// (?:) 在正则中不会捕捉相关子串, 但可以对其子串合并
if (!p1)
return ""
const arr = p1.split('.')
let datas = data
arr.forEach(function (value, index) {
datas = datas[value] || null
})
return datas
})
}
// 相关调用方法
const templateStr = `{{name}}`
const newStr = template(templateStr, {name:"成功"})
console.log(newStr)

模板内容替换成功
success

Javascript Study Notes

发表于 2017-05-09   |   分类于 日志

function

Function declaration statements

1
2
3
function funcname({arguments}) {
// return some statements
}

Function definition expressions

1
2
3
var funcname = function({arguments}) {
// return some statements
}

Function declaration statements may only appear at the top level of the funtion they are nested within.That is, function definitions may not appear within if statements, while loop, or any other statements.
The function declaration statement also declares the function name as a variable and assigns the function object to it. Like variables declared with var, functions defined with function definition statements are implicitly “hoisted” to the top of the containing script or function, so that they are visible throughout the script or function. With var, only the variable declaration is hoisted—the variable initialization code remains where you
placed it.
Both the function name and the function body are hoisted: all functions in a script or all nested functions in a function are declared before any other code is run. This means that you can invoke a Javascript function before you declare it.
Like the var statement, function declaration statements create variables that cannot be deleted. These variables are not read-only, however, and their value can be overwritten

prototype

deepclone

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Object.prototype.deepClone = function(top) {
var Obj = [];
if(!top)
top = this;
for(var key in this) {
if(top === this[key]) {
return;
}
Obj[key] = typeof this[key] === 'object'
? Obj[key] = this[key].deepClone(top)
: Obj[key] = this[key];
}
return Obj;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Object.prototype.deepClone = function(top) {
var Obj = [];
if(!top) {
top = this;
}
for(var key in this) {
if(top === this[key]) {
return;
}
if(typeof this[key] === 'object' ){
Obj[key] = this[key].deepClone(top)
} else if(typeof this[key] === 'function' &&
this[key] !== Object.prototype.deepClone){
// will find the deepClone function in the prototype.Object
var func = function () {};
func.prototype = this[key].deepClone(top);
Obj[key] = new this[key];
Obj[key].prototype = new func;
} else {
Obj[key] = this[key];
}
}
return Obj;
}

bind

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Function.prototype.bind = function (oThis) {
if (typeof this !== "function") {
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
}

var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function () {},
fBound = function () {
return fToBind.apply(
this instanceof fNOP && oThis ? this : oThis || window,
aArgs.concat(Array.prototype.slice.call(arguments))
);
};

fNOP.prototype = fToBind.prototype;
fBound.prototype = new fNOP();
return fBound;
};

Winter Holiday Plan

发表于 2017-01-12   |   分类于 日志

寒假计划 2017.1.12 ~ 2017.2.12

-[X] Graphql 2017.1.14 ~ 2017.1.18

1
2
3
4
5
6
7
// obj 代表这个函数返回的对象
// param 代表传入函数的参数
// context 代表对象注入的上下文
// info 函数的状态
resolve: function(obj, param, context, info) {
// return obj;
}

-[X] mongodb 2017.1.20 ?

Robomongo、Mongochef用作MongoDB可视化工具
后台运行需添加日志路径
mongoess, mongolass 作为连接mongodb的中间件

-[X] node 2017.1.14 ?

nvm、n管理node版本
nrm切换npm源

-[X] memcached 2017.1.19 ?
-[X] docker

images显示镜像, ps显示容器, rm删除容器, rmi删除镜像, tag标记
run commit build 相关命令
容器云保证最小开销,易扩展
数据保留在数据卷中,容器由Dockerfile创建

-[ ] es2015,es2016 2017.1.14 ?
-[ ] origami studio

节点动效软件

-[X] koa/express 2017.1.14 ?

koa后面再说吧。。
关于express中headerSent公共属性与304缓存
express中使用res.headerSent公共属性判断是否发送了header,
我们知道header是早于body的,所以当我们在发送200头时,
client端才认为我们达成了一次正确请求,
当我在express中使用中间件的headerSent属性是否为true来判断是否发送了协议头,
却发现如下报错

Error: Can't render headers after they are sent to the client.

这是由于当我们请求时,如果缓存未过期,返回304协议头,将会不再进入路由,
但如果我们在此之前使用中间件判断时会出现headerSent为false的情况,
所以应当在中间件判断headerSent的位置判断协议头是否不为304
-[X] 函数式编程
函数一等公民, 一切变量都是函数
闭包, 调用外部函数中变量
内存泄漏, 父函数在闭包函数被释放前无法释放
回调, 函数入参
原型链
-[X] node单元测试
mocha是一种js测试框架:
describe是Test Suite的开始, 可相互嵌套,
it是测试模块,
before在beforeEach执行之前同步执行
beforeEach在每个it执行之前执行
afterEach在每个it执行之后执行
after在afterEach执行之后同步执行
xit, it.skip, 空函数 都会pending测试
supertest should istanbul
-[ ] 深度学习

安卓签名打包成Apk

发表于 2016-06-07   |   分类于 code

1.使用keytool命令生成签名密钥

1
keytool -genkey -v -keystore my-release-key.keystore  -alias my-key-alias -keyalg RSA -keysize 2048 -validity 10000

其中my-release-key.keystore是将要生成的文件名, my-key-alias也是自己取的名称, 都可以修改, 在之后会使用这两个名称, 所以需要记录一下
之后会询问你密钥库口令和密钥口令(两个密码可以不同,之后密钥口令也会用到,所以也不要忘记),以及相关的签名信息,直接填写即可

1+.使用Android Studio IDE 生成密钥

菜单中选择build->Generate Signed APK,
之后点击[Create new…],
直接填写相关信息即可,
最后会生成.jks结尾的文件,.jks和.keystore都可以用作签名,所以使用上没有区别

2.编辑android下的gradle.properties文件

添加如下代码

1
2
3
4
MYAPP_RELEASE_STORE_FILE=my-release-key.keystore    // 之前生成的文件名
MYAPP_RELEASE_KEY_ALIAS=my-key-alias // 生成签名时的alias名称
MYAPP_RELEASE_STORE_PASSWORD=store_password // 密钥库口令
MYAPP_RELEASE_KEY_PASSWORD=password // 密钥口令

3.编辑工程文件中的android/app/build.gradle文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
...  
android {
...
defaultConfig { ... }
signingConfigs {
release {
storeFile file(MYAPP_RELEASE_STORE_FILE)
storePassword MYAPP_RELEASE_STORE_PASSWORD
keyAlias MYAPP_RELEASE_KEY_ALIAS
keyPassword MYAPP_RELEASE_KEY_PASSWORD
}
}
buildTypes {
release {
...
signingConfig signingConfigs.release
}
}
}
…

4.在android的目录下使用dos命令行

1
gradlew assembleRelease

5.该命令运行结束之后,会在android/app/build/outputs/apk目录下面生成app-release.apk文件,即可用于发布

数据缓存及同步数据

发表于 2016-05-23   |   分类于 code

之前接的项目还有个有趣的需求, 是根据Id, 获得这一天之内所有关于这个Id的操作(类似于用户状态, 登录的Ip分布), 再以特定的数组形式输出到前台.即每次请求都会根据Id获取它所有的数据, 每个Id一天的数据量大概在3000条左右..我们的服务器是1M带宽, 1M内存的学生机..在MySql出现瓶颈之前, 服务器本身已经有点难以承担这个数据量了..

为了解决频繁请求数据库的问题, 于是自己写了个缓存, 本身是基于thinkphp框架写的网站:

首先获取Id和日期, 调用 cache 方法

1
2
3
4
5
6
7
8
9
class IndexController extends Controller
{
public function index()
{
$Id = I('get.Id');
$date = I('get.date')?I('get.date'):date('Y-m-d');
$cacheArray = $this->cache($date, $Id);
}
}

之后是 cache 的实现, 其中调用用于查询日期的cacheVerify函数和组合数据信息的cachePush函数

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
private function cache($date, $Id)
{
// 分解日期
$dateArray = explode('-', $date);
// 判断年所代表的文件夹是否存在
if (!is_dir('./Public/Cache/' . $dateArray[0] . '/'))
// 不存在则生成以年命名的文件夹
mkdir('./Public/Cache/' . $dateArray[0] . '/', 0777, 1);
// 判断月所代表的文件夹是否存在
if (!is_dir('./Public/Cache/' . $dateArray[0] . '/' . $dateArray[1] . '/'))
// 不存在则生成以月命名的文件夹
mkdir('./Public/Cache/' . $dateArray[0] . '/' . $dateArray[1] . '/', 0777, 1);
// 添加以日命名的文件信息
$file = './Public/Cache/' . $dateArray[0] . '/' . $dateArray[1] . '/' . $dateArray[2] . '.json';
// 以读写形式打开, 同时基于PHP的特性, 如果文件不存在则会自动新建
$files = fopen($file, 'a+');
// 读取文件中的所有信息
$files_json = fread($files, filesize($file));
// 以 json_decode 的方法转为 array 数组
$files_data = json_decode($files_json, true);
// 调用用于查询日期下的数据是否是最新的cacheVerify函数
if (false == $this->cacheVerify($files_data, $date)) {
// 不为最新则调用生成缓存的 cachePush 函数
$query = $this->cachePush($files_data, $date, $pondId);
// 将生成的缓存数组 array 转为json,
// 同时JSON_UNESCAPED_UNICODE 变量用于将unicode转码,
// 但前提是PHP版本高于5.4
$json = json_encode($query, JSON_UNESCAPED_UNICODE);
// 将生成的缓存信息写入文件中
fwrite($files, $json);
}
// 关闭已打开的文件夹
fclose($files);
// 返回产生的缓存数组
return $query;
}

cacheVerify 的函数和cachePush 函数, 每次判断一下最新的ID和缓存中的ID是否相同, 如果相同则认为缓存是最新, 如果不同则需要更新缓存文件

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
private function cacheVerify(array $files_data, $date)
{
$lastId = M()->query('SELECT id FROM device_data WHERE locate("' . $date . '",recv_time) ORDER BY id DESC LIMIT 1');
$files_data_end=end($files_data);
if ($files_data_end['id'] == $lastId[0]['id'])
return true;
else
return false;
}
private function cachePush(array $files_data, $date, $pondId)
{
$sql_select = '
SELECT
DATA.id,
DATA.recv_time,
FROM
device_data AS DATA
JOIN device_depend AS depend ON depend.pondId =' . $pondId . '
AND depend.deviceId = DATA.device_id
AND depend.group_id = DATA.group_id
WHERE
LOCATE("' . $date . '", recv_time)
';

$files_data_end=end($files_data);
if (!empty($files_data_end['id'])) {
$sql_select .= " AND DATA.id>" . $files_data_end['id'] . ';';
}
$query = M()->query($sql_select);
foreach($query as $key=> $value){
$query[$key]['recv_time']=str_replace($date.' ',"",$value['recv_time']);
}
if (is_array($files_data)) {
$query = array_merge($files_data, $query);
}
return $query;
}

关于PHP监听数据库的定时器

发表于 2016-05-23   |   分类于 code

今天接触到一个项目, 需要在新增了数据之后监听字段, 如果超过 3 秒字段还不变化的话直接输出操作超时, 需要利用PHP完成一个实时监听数据

完成思路主要是每秒发送一个请求查询字符串是否变化, 当超过 3 秒或者字段变化时跳出循环, 以下是具体实现

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
class packet
{
private $lastId;
private $DB;
...
private function verify()
{
$sql_select = 'SELECT count(*) FROM raw_command WHERE id=:id AND ack_time IS NOT NULL ;';
// 已封装的查询方法, 使用PDO的prepare方法, 预保留字段.
$data = $this->DB->selectQuery($sql_select, [':id' => $this->lastId]);
// 返回字段是否变化.
return count($data);
}
public function timeKey()
{
$i = 0;
while ($i != 3) {
if ($this->verify()) {
return true;
}
$i++;
sleep(1);// 等待1s
};
return false;
}
...
}

调用时, 直接调用 packet 的 timeKey 方法

Hello World

发表于 2016-03-16

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

Quick Start

Create a new post

1
$ hexo new "My New Post"

More info: Writing

Run server

1
$ hexo server

More info: Server

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

More info: Deployment

新的开始

发表于 2016-03-16   |   分类于 日志

这是新的开始,我用hexo创建了第一篇文章。

通过下面的命令,就可以创建新文章

1
2
D:\workspace\javascript\nodejs-hexo>hexo new 新的开始
[info] File created at D:\workspace\javascript\nodejs-hexo\source\_posts\新的开始.md

感觉非常好。

shenshun

shenshun

10 日志
2 分类
10 标签
© 2017 shenshun
由 Hexo 强力驱动
主题 - NexT.Muse