几个有趣的算法

根据身份证号码计算出性别、年龄

一、身份证号码国家标准

1、范围

《公民身份号码》(GB11643-1999)该标准规定了公民身份号码的编码对象、号码的结构和表现形式,使每个编码对象获得一个唯一的、不变的法定号码。

2、号码的结构

公民身份号码是特征组合码,由十七位数字本体码和一位校验码组成。排列顺序从左至右依次为:六位数字地址码,八位数字出生日期码,三位数字顺序码和一位数字校验码。

img

img

2.1、地址码

表示编码对象常住户口所在县(市、旗、区)的行政区划代码

2.2、出生日期码

表示编码对象出生的年、月、日

2.3、顺序码

表示在同一地址码所标识的区域范围内,对同年、同月、同日出生的人编定的顺序号,顺序码的奇数分配给男性,偶数分配给女性。

2.4、校验码

根据前面十七位数字码,按照 ISO 7064:1983.MOD 11-2 中的校验码计算方法计算确定

(1)十七位数字本体码加权求和公式:S = Sum(Ai * Wi)

img

 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)通过模得到对应的校验码

img

模为 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 运行结果:

img

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 运行结果:

img

贪心算法

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)
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇