根据身份证号码计算出性别、年龄
一、身份证号码国家标准
1、范围
《公民身份号码》(GB11643-1999)该标准规定了公民身份号码的编码对象、号码的结构和表现形式,使每个编码对象获得一个唯一的、不变的法定号码。
2、号码的结构
公民身份号码是特征组合码,由十七位数字本体码和一位校验码组成。排列顺序从左至右依次为:六位数字地址码,八位数字出生日期码,三位数字顺序码和一位数字校验码。
2.1、地址码
表示编码对象常住户口所在县(市、旗、区)的行政区划代码
2.2、出生日期码
表示编码对象出生的年、月、日
2.3、顺序码
表示在同一地址码所标识的区域范围内,对同年、同月、同日出生的人编定的顺序号,顺序码的奇数分配给男性,偶数分配给女性。
2.4、校验码
根据前面十七位数字码,按照 ISO 7064:1983.MOD 11-2 中的校验码计算方法计算确定
(1)十七位数字本体码加权求和公式:S = Sum(Ai * Wi)
S = 7 + 9 + 0 + 5 + 0 + 20 + 2 + 9 + 24 + 27 + 7 + 18 + 30 + 5 + 0 + 0 + 4 = 167
(2)计算模:Y = mod(S, 11) Y = 167 % 11 => 2
(3)通过模得到对应的校验码
模为 2 时,校验码为 X。
二、代码实现
1、身份证号正确性校验
const WEIGHT = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
const MO = [1, 0, "X", 9, 8, 7, 6, 5, 4, 3, 2];
function isRightId(id) {
const arr = id.split("");
const checkNumber = arr.pop();
// 去除校验码,将 pop 的返回值赋值给 checkNumber
let sum = 0;
arr.forEach((ele, index) => {
sum += ele * WEIGHT[index];
});
const m = sum % 11;
const result = MO[m];
return result + "" === checkNumber;
}
console.log(isRightId("11010519491231002X")); // true
console.log(isRightId("110105194912310029")); // false
2、由身份证号计算年龄
function getAge(id) {
// 1、先判断身份证号的正确性
// 2、判断是否在世
const year = id.substr(6, 4);
const month = id.substr(10, 2);
const day = id.substr(12, 2);
const timeBrth = new Date(`{year}/{month}/{day}`).getTime();
const timeNow = new Date().getTime();
const longTime = timeNow - timeBrth;
const days = longTime / (1 * 24 * 60 * 60 * 1000);
let result = "";
if (days<31) {
result = parseInt(days) + "天";
} else if (days<365) {
result = `{parseInt(days / 30)}月{parseInt(days % 30)}天`;
} else {
result = `{parseInt(days / 365)}岁{parseInt(
(days % 365) / 30
)}月{parseInt((days % 365) % 30)}天`;
}
return result;
}
console.log(getAge("11010519491231002X"));
// 71 岁 8 月 16 天
console.log(getAge("11010520210820002X")); // 6 天
console.log(getAge("11010520210720002X")); // 1 月 7 天
3、由身份证号判断性别
function getSex(id){
// 1、先判断身份证号的正确性
const sex = id.substr(16,1)
return sex%2? '男': '女'
}
console.log(getSex('11010519491231002X')) // 女
console.log(getSex('11010520210820001X')) // 男
三、其他
1、变性手术后,身份证号码是否更改?
跨性别人士身份证性别变更后,依户口所在派出所公示为准,进行身份证号码变更。
2、计算年龄前应先确认是否在世。
四、参考资料
《公民身份号码》(GB11643-1999)
动态规划
1、定义
动态规划(Dynamic Programming,DP)是运筹学的一个分支,是求解决策过程最优化的过程。
可以简单的理解为是对传统递归的一种优化。在 DP 的实践中很重要的就是递推关系和边界条件。
2、简单:爬楼梯
题目
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意 给定 n 是一个正整数。
输入:2 输出:2 解释: 有两种方法可以爬到楼顶。 1. 1 阶 + 1 阶 2. 2 阶
示例 2:
输入:3 输出:3 解释: 有三种方法可以爬到楼顶。 1. 1 阶 + 1 阶 + 1 阶 2. 1 阶 + 2 阶 3. 2 阶 + 1 阶
代码:
// 把数据缓存在一个数组中
var climbStairs = function (n) {
let dp = [];
dp[0] = 1;
dp[1] = 1;
for (let i = 2; i <= n; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
};
// 使用递归
var climbStairs = function (n) {
if (n === 1) return 1;
if (n === 2) return 2;
return climbStairs(n - 1) + climbStairs(n - 2);
};
思路:
f(x)=f(x?1)+f(x_?2) 爬到第 x _级台阶的方案数是爬到第 x – 1 级台阶的方案数和爬到第 x – 2 级台阶的方案数的和。
LeetCode 运行结果:
3、中等:最长回文子串
题目
给你一个字符串 s,找到 s 中最长的回文子串。
示例 1:
输入:s = "babad" 输出:"bab" 解释:"aba" 同样是符合题意的答案。
示例 2:
输入:s = "cbbd" 输出:"bb"
思路:
当 s[i+1 : j-1] 是回文串,并且 s 的第 i 和 j 个字母相同时,s[i:j] 才会是回文串。
即:P(i,j)=P(i+1,j?1) 且 (Si Sj)。
边界条件:子串的长度为 1 或 2。对于长度为 1 的子串,它显然是个回文串;对于长度为 2 的子串,只要它的两个字母相同,它就是一个回文串。
- P(i, i) = true
- P(i, i+1) = (Si Si+1)
代码:
function longestPalindrome(s) {
// 先判断字符串长度,如果为 1 则直接返回
let len = s.length;
if (len < 2) return s;
// 初始化变量
let maxLen = 1;
let begin = 0;
// dp[i][j] 表示 s[i..j] 是否是回文串
let dp = [];
// 初始化:所有长度为 1 的子串都是回文串
for (let i = 0; i < len; i++) {
dp[i] = [];
dp[i][i] = true;
}
// 将字符串切割为数组
let charArray = s.split("");
// 递推开始
for (let L = 2; L <= len; L++) {
// 枚举子串长度
// 枚举左边界,左边界的上限设置可以宽松一些
for (let i = 0; i < len; i++) {
// 由 L 和 i 可以确定右边界,即 j - i + 1 = L 得
let j = L + i - 1; // 如果右边界越界,退出当前循环
if (j >= len) {
break;
} // 判断是否为回文
if (charArray[i] !== charArray[j]) {
dp[i][j] = false;
} else {
// 对于一个子串而言,如果它是回文串,并且长度大于 2,那么将它首尾的两个字母去除之后,它仍然是个回文串。
let flag = j - i < 3;
dp[i][j] = flag ? true : dp[i + 1][j - 1];
}
// 当 dp[i][L] == true 成立,就表示子串 s[i..L] 是回文,记录回文长度和起始位置
if (dp[i][j] && j - i + 1 > maxLen) {
maxLen = j - i + 1;
begin = i;
}
}
}
return s.substring(begin, begin + maxLen);
}
console.log(longestPalindrome("babad"), "babad");
// bab babad
console.log(longestPalindrome("cbbd"), "cbbd"); // bb cbbd
LeetCode 运行结果:
贪心算法
1、定义
在对问题求解时,总是做出在当前看来是最好的选择。
2、分饼干
假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。
示例 1: 输入: g = [1,2,3], s = [1,1] 输出: 1
示例 2: 输入: g = [1,2], s = [1,2,3] 输出: 2
function findContentChildren1(children, cookies) {
children = children.sort((a, b) => a - b);
cookies = cookies.sort((a, b) => a - b);
let childrenLength = children.length;
let cookiesLength = cookies.length;
let count = 0;
for (let i = 0, j = 0; i < childrenLength && j < cookiesLength; i++, j++) {
while (j < cookiesLength && children[i] > cookies[j]) {
j++;
}
if (j < cookiesLength) {
count++;
}
}
return count;
}
console.log(findContentChildren1([1, 2, 3], [1, 1])); // 1
console.log(findContentChildren1([1, 2], [1, 2, 3])); // 2
console.log(findContentChildren1([1, 2, 3], [1, 1, 3, 4])); // 3
核心思想:
- 将孩子的胃口、饼干的大小都按照从小到大排序。
- for 循环遍历,比较孩子的胃口 children[i]和饼干的大小 cookies[j]之间的关系,当当前饼干不能满足孩子的胃口时,选择下一个饼干进行比较。
- 如果满足孩子的胃口,且 j 在范围内,则 count 加 1。
代码解读:
- 定义函数 findContentChildren,接受 2 个参数,分别为 children:孩子的胃口,cookies 饼干的大小。
- 将 children 和 cookies 按照从小到大排序。
- 定义能够满足孩子胃口的个数 count,并最终将 count 返回。
- 循环遍历,当 children[i] > cookies[j] 即当前饼干不能满足孩子时,j++选择下一块饼干进行比较。
- 如果 children[i] <= cookies[j] 即当前饼干能满足孩子,且 j 在范围内,则 count 加 1。
- 进入下一次循环(即尝试满足下一个孩子)。
3、买股票
给定一个整数数组 prices,其中第 i 个元素代表了第 i 天的股票价格 ;整数 fee 代表了交易股票的手续费用。你可以无限次地完成交易,但是你每笔交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。返回获得利润的最大值。注意:这里的一笔交易指买入持有并卖出股票的整个过程,每笔交易你只需要为支付一次手续费。
示例 1:输入:prices = [1, 3, 2, 8, 4, 9], fee = 2, 输出:8
解释:
能够达到的最大利润:
在此处买入 prices[0] = 1 在此处卖出 prices[3] = 8 在此处买入 prices[4] = 4 在此处卖出 prices[5] = 9 总利润: ((8 – 1) – 2) + ((9 – 4) – 2) = 8
示例 2:输入:prices = [1,3,7,5,10,3], fee = 3 输出:6
function maxProfit(list, fee) {
const length = list.length;
let buy = list[0] + fee; // 假定:买入时机为第 1 天
let profit = 0;
for (let i = 1; i < length; i++) {
if (list[i] + fee < buy) {
// 如果股票价格降低,则调整买入时机
buy = list[i] + fee;
} else if (list[i] > buy) {
// 如果有利润,则卖出
profit += list[i] - buy; // 计算利润
buy = list[i]; // 调整买入时机
}
}
return profit;
}
console.log(maxProfit([1, 3, 2, 8, 4, 9], 2)); // 8
console.log(maxProfit([1, 3, 7, 5, 10, 3], 3)); // 6
代码解读:
- 定义函数 maxProfit,接受 2 个参数,list 为股票价格趋势,fee 为手续费
- 定义 profit,并在最后将 profit 返回
- 假定:买入时机为第 1 天(即:list[0])
- 如果股票价格降低,则调整买入时机为后一天
- 如果有利润,则卖出,并计算利润,再次调整买入时机(即循环步骤 3、4、5)
核心思想:
- 假定:买入时机为第 1 天
- 如果股票价格降低,则调整买入时机为后一天
- 如果有利润,则卖出,并且计算利润
- 再次 – 假定:买入时机(循环步骤 1-3)