当前位置: 首页 > news >正文

Rust 练习册 :Luhn与校验算法

在现代金融和数据处理系统中,确保数据的准确性至关重要。Luhn算法(也称为模10算法)是一种简单的校验和算法,广泛用于验证各种识别号码,如信用卡号、IMEI号码等。在 Exercism 的 “luhn” 练习中,我们需要实现Luhn算法来验证给定的字符串是否符合校验规则。这不仅能帮助我们掌握字符串处理和数学计算技巧,还能深入学习Rust中的字符处理、错误处理和算法实现。

什么是Luhn算法?

Luhn算法是由IBM科学家Hans Peter Luhn在1954年发明的一种校验和算法,用于验证各种识别号码的准确性。它主要用于信用卡号、IMEI号码、加拿大社会保险号等。

Luhn算法的工作原理如下:

  1. 从右到左,对偶数位置的数字乘以2(第二个、第四个、第六个数字等)
  2. 如果乘以2的结果大于9,则将结果的各位数字相加(或减去9)
  3. 将所有数字相加
  4. 如果总和能被10整除,则该号码有效

例如,验证号码"4539 3195 0343 6467":

  1. 移除空格得到"4539319503436467"
  2. 从右到左,对偶数位置的数字乘以2:
    • 7 6 4 3 4 0 5 9 1 3 9 5 3 4 5 4
    • 7 12 4 6 4 0 5 18 1 6 9 10 3 8 5 8
  3. 大于9的数字减去9:
    • 7 3 4 6 4 0 5 9 1 6 9 1 3 8 5 8
  4. 求和:7+3+4+6+4+0+5+9+1+6+9+1+3+8+5+8 = 90
  5. 90能被10整除,因此号码有效

让我们先看看练习提供的函数签名:

/// Check a Luhn checksum.
pub fn is_valid(code: &str) -> bool {unimplemented!("Is the Luhn checksum for {} valid?", code);
}

我们需要实现一个函数,验证给定的字符串是否符合Luhn校验规则。

设计分析

1. 核心要求

  1. 字符处理:处理数字和空格字符
  2. 位置计算:从右到左计算数字位置
  3. 数学运算:执行乘法和加法运算
  4. 校验和验证:验证总和是否能被10整除
  5. 输入验证:处理无效字符和格式

2. 技术要点

  1. 字符串处理:过滤和转换字符
  2. 逆序处理:从右到左处理数字
  3. 数学计算:执行校验和计算
  4. 边界情况:处理各种无效输入

完整实现

1. 基础实现

/// Check a Luhn checksum.
pub fn is_valid(code: &str) -> bool {// 移除空格并验证字符let cleaned: String = code.chars().filter(|c| !c.is_whitespace()).collect();// 检查长度和字符有效性if cleaned.len() <= 1 || !cleaned.chars().all(|c| c.is_ascii_digit()) {return false;}let digits: Vec<u32> = cleaned.chars().map(|c| c.to_digit(10).unwrap()).collect();let mut sum = 0;let len = digits.len();for (i, &digit) in digits.iter().enumerate() {// 从右到左,偶数位置的数字需要乘以2if (len - i) % 2 == 0 {let doubled = digit * 2;sum += if doubled > 9 { doubled - 9 } else { doubled };} else {sum += digit;}}sum % 10 == 0
}

2. 优化实现

/// Check a Luhn checksum.
pub fn is_valid(code: &str) -> bool {let digits: Vec<u32> = code.chars().filter(|c| !c.is_whitespace()).map(|c| c.to_digit(10)).collect::<Option<Vec<u32>>>().unwrap_or_default();// 必须至少有2位数字if digits.len() <= 1 {return false;}let sum: u32 = digits.iter().rev() // 从右到左处理.enumerate().map(|(i, &digit)| {if i % 2 == 1 { // 偶数位置(从右数第2、4、6...位)let doubled = digit * 2;if doubled > 9 { doubled - 9 } else { doubled }} else {digit}}).sum();sum % 10 == 0
}

3. 高性能实现

/// Check a Luhn checksum.
pub fn is_valid(code: &str) -> bool {let mut digits = Vec::new();// 验证并收集数字for c in code.chars() {if c.is_whitespace() {continue;}if let Some(digit) = c.to_digit(10) {digits.push(digit);} else {return false; // 包含非数字字符}}// 至少需要2位数字if digits.len() <= 1 {return false;}let mut sum = 0u32;// 从右到左处理for (i, &digit) in digits.iter().rev().enumerate() {if i % 2 == 1 { // 偶数位置(从右数第2、4、6...位)let doubled = digit * 2;sum += if doubled > 9 { doubled - 9 } else { doubled };} else {sum += digit;}}sum % 10 == 0
}

测试用例分析

通过查看测试用例,我们可以更好地理解需求:

#[test]
fn test_single_digit_strings_can_not_be_valid() {process_valid_case("1", false);
}

单个数字不能通过验证。

#[test]
fn test_a_single_zero_is_invalid() {process_valid_case("0", false);
}

单个零也不能通过验证。

#[test]
fn test_a_simple_valid_sin_that_remains_valid_if_reversed() {process_valid_case("059", true);
}

有效的简单号码,即使反转也有效。

#[test]
fn test_a_valid_canadian_sin() {process_valid_case("055 444 285", true);
}

有效的加拿大社会保险号。

#[test]
fn test_invalid_credit_card() {process_valid_case("8273 1232 7352 0569", false);
}

无效的信用卡号。

#[test]
fn strings_that_contain_non_digits_are_invalid() {process_valid_case("055a 444 285", false);
}

包含非数字字符的字符串无效。

#[test]
fn test_more_than_a_single_zero_is_valid() {process_valid_case("0000 0", true);
}

多个零可以是有效的。

#[test]
fn test_using_ascii_value_for_doubled_nondigit_isnt_allowed() {process_valid_case(":9", false);
}

不能使用非数字字符的ASCII值进行计算。

性能优化版本

考虑性能的优化实现:

/// Check a Luhn checksum.
pub fn is_valid(code: &str) -> bool {let mut digits = Vec::with_capacity(code.len());// 预分配并验证字符for c in code.chars() {match c {' ' => continue, // 跳过空格'0'..='9' => digits.push(c as u32 - '0' as u32),_ => return false, // 非数字字符}}// 至少需要2位数字if digits.len() <= 1 {return false;}let mut sum = 0u32;// 从右到左处理,使用索引计算位置for (i, &digit) in digits.iter().rev().enumerate() {if i % 2 == 1 { // 偶数位置(从右数第2、4、6...位)let doubled = digit * 2;sum += if doubled > 9 { doubled - 9 } else { doubled };} else {sum += digit;}}sum % 10 == 0
}// 使用字节数组的高性能版本
pub fn is_valid_bytes(code: &str) -> bool {let bytes = code.as_bytes();let mut digits = Vec::with_capacity(bytes.len());for &byte in bytes {match byte {b' ' => continue,b'0'..=b'9' => digits.push((byte - b'0') as u32),_ => return false,}}if digits.len() <= 1 {return false;}let mut sum = 0u32;for (i, &digit) in digits.iter().rev().enumerate() {if i % 2 == 1 {let doubled = digit * 2;sum += if doubled > 9 { doubled - 9 } else { doubled };} else {sum += digit;}}sum % 10 == 0
}

错误处理和边界情况

考虑更多边界情况的实现:

#[derive(Debug, PartialEq)]
pub enum LuhnError {TooShort,InvalidCharacter(char),EmptyInput,
}pub fn validate_luhn_detailed(code: &str) -> Result<bool, LuhnError> {if code.is_empty() {return Err(LuhnError::EmptyInput);}let mut digits = Vec::new();for c in code.chars() {if c.is_whitespace() {continue;}match c.to_digit(10) {Some(digit) => digits.push(digit),None => return Err(LuhnError::InvalidCharacter(c)),}}if digits.len() <= 1 {return Err(LuhnError::TooShort);}let sum: u32 = digits.iter().rev().enumerate().map(|(i, &digit)| {if i % 2 == 1 {let doubled = digit * 2;if doubled > 9 { doubled - 9 } else { doubled }} else {digit}}).sum();Ok(sum % 10 == 0)
}/// Check a Luhn checksum.
pub fn is_valid(code: &str) -> bool {validate_luhn_detailed(code).unwrap_or(false)
}// 支持自定义起始位置的版本
pub fn is_valid_custom_start(code: &str, start_from_right: bool) -> bool {let digits: Result<Vec<u32>, _> = code.chars().filter(|c| !c.is_whitespace()).map(|c| c.to_digit(10)).collect();let digits = match digits {Ok(d) if d.len() > 1 => d,_ => return false,};let sum: u32 = if start_from_right {digits.iter().rev().enumerate().map(|(i, &digit)| {if i % 2 == 1 {let doubled = digit * 2;if doubled > 9 { doubled - 9 } else { doubled }} else {digit}}).sum()} else {digits.iter().enumerate().map(|(i, &digit)| {if i % 2 == 1 {let doubled = digit * 2;if doubled > 9 { doubled - 9 } else { doubled }} else {digit}}).sum()};sum % 10 == 0
}

扩展功能

基于基础实现,我们可以添加更多功能:

pub struct LuhnValidator;impl LuhnValidator {pub fn new() -> Self {LuhnValidator}/// 验证Luhn校验和pub fn is_valid(&self, code: &str) -> bool {self.validate(code).is_ok()}/// 详细验证并返回结果pub fn validate(&self, code: &str) -> Result<LuhnValidationResult, LuhnError> {if code.is_empty() {return Err(LuhnError::EmptyInput);}let mut digits = Vec::new();let mut original_chars = Vec::new();for c in code.chars() {original_chars.push(c);if c.is_whitespace() {continue;}match c.to_digit(10) {Some(digit) => digits.push(digit),None => return Err(LuhnError::InvalidCharacter(c)),}}if digits.len() <= 1 {return Err(LuhnError::TooShort);}let sum: u32 = digits.iter().rev().enumerate().map(|(i, &digit)| {if i % 2 == 1 {let doubled = digit * 2;if doubled > 9 { doubled - 9 } else { doubled }} else {digit}}).sum();let is_valid = sum % 10 == 0;Ok(LuhnValidationResult {is_valid,digit_count: digits.len(),checksum: sum,original_input: code.to_string(),})}/// 生成符合Luhn算法的校验位pub fn generate_check_digit(&self, partial_code: &str) -> Option<u32> {let digits: Result<Vec<u32>, _> = partial_code.chars().filter(|c| !c.is_whitespace()).map(|c| c.to_digit(10)).collect();let digits = digits.ok()?;// 添加一个占位符作为校验位let mut extended_digits = digits.clone();extended_digits.push(0);let sum: u32 = extended_digits.iter().rev().enumerate().map(|(i, &digit)| {if i % 2 == 1 {let doubled = digit * 2;if doubled > 9 { doubled - 9 } else { doubled }} else {digit}}).sum();let check_digit = (10 - (sum % 10)) % 10;Some(check_digit)}/// 为部分代码添加校验位pub fn append_check_digit(&self, partial_code: &str) -> Option<String> {let check_digit = self.generate_check_digit(partial_code)?;Some(format!("{}{}", partial_code, check_digit))}/// 批量验证多个代码pub fn validate_batch(&self, codes: &[&str]) -> Vec<(String, bool)> {codes.iter().map(|&code| (code.to_string(), self.is_valid(code))).collect()}
}#[derive(Debug)]
pub struct LuhnValidationResult {pub is_valid: bool,pub digit_count: usize,pub checksum: u32,pub original_input: String,
}#[derive(Debug, PartialEq)]
pub enum LuhnError {TooShort,InvalidCharacter(char),EmptyInput,
}// 便利函数
pub fn is_valid(code: &str) -> bool {LuhnValidator::new().is_valid(code)
}

实际应用场景

Luhn算法在实际开发中有以下应用:

  1. 支付系统:验证信用卡号和借记卡号
  2. 移动设备:验证IMEI号码
  3. 身份验证:验证各种身份证件号码
  4. 数据清洗:检测和纠正输入错误
  5. 表单验证:在用户输入时实时验证
  6. 库存管理:验证产品序列号
  7. 银行系统:验证账户号码
  8. 电商平台:验证支付信息

算法复杂度分析

  1. 时间复杂度:O(n)

    • 需要遍历字符串中的每个字符,其中n是字符串长度
  2. 空间复杂度:O(n)

    • 需要存储过滤后的数字数组

与其他实现方式的比较

// 使用函数式编程的实现
pub fn is_valid_functional(code: &str) -> bool {let digits: Option<Vec<u32>> = code.chars().filter(|c| !c.is_whitespace()).map(|c| c.to_digit(10)).collect();let digits = match digits {Some(d) if d.len() > 1 => d,_ => return false,};let sum: u32 = digits.iter().rev().enumerate().map(|(i, &digit)| {if i % 2 == 1 {let doubled = digit * 2;if doubled > 9 { doubled - 9 } else { doubled }} else {digit}}).sum();sum % 10 == 0
}// 使用递归的实现
pub fn is_valid_recursive(code: &str) -> bool {fn validate_recursive(chars: &[char], digits: &mut Vec<u32>) -> bool {for &c in chars {if !c.is_whitespace() {match c.to_digit(10) {Some(digit) => digits.push(digit),None => return false,}}}if digits.len() <= 1 {return false;}let sum: u32 = digits.iter().rev().enumerate().map(|(i, &digit)| {if i % 2 == 1 {let doubled = digit * 2;if doubled > 9 { doubled - 9 } else { doubled }} else {digit}}).sum();sum % 10 == 0}let chars: Vec<char> = code.chars().collect();let mut digits = Vec::new();validate_recursive(&chars, &mut digits)
}// 使用正则表达式的实现
use regex::Regex;pub fn is_valid_regex(code: &str) -> bool {let re = Regex::new(r"[^\d\s]").unwrap();if re.is_match(code) {return false;}let digits: Vec<u32> = code.chars().filter(|c| c.is_ascii_digit()).map(|c| c.to_digit(10).unwrap()).collect();if digits.len() <= 1 {return false;}let sum: u32 = digits.iter().rev().enumerate().map(|(i, &digit)| {if i % 2 == 1 {let doubled = digit * 2;if doubled > 9 { doubled - 9 } else { doubled }} else {digit}}).sum();sum % 10 == 0
}// 使用迭代器链的实现
pub fn is_valid_iterator(code: &str) -> bool {code.chars().filter(|c| !c.is_whitespace()).try_fold((Vec::new(), true), |(mut digits, valid), c| {if !valid {return Ok((digits, false));}match c.to_digit(10) {Some(digit) => {digits.push(digit);Ok((digits, true))}None => Ok((digits, false))}}).map(|(digits, valid)| {if !valid || digits.len() <= 1 {return false;}digits.iter().rev().enumerate().map(|(i, &digit)| {if i % 2 == 1 {let doubled = digit * 2;if doubled > 9 { doubled - 9 } else { doubled }} else {digit}}).sum::<u32>() % 10 == 0}).unwrap_or(false)
}

总结

通过 luhn 练习,我们学到了:

  1. 字符串处理:掌握了字符过滤、转换和验证技巧
  2. 算法实现:学会了实现经典的校验和算法
  3. 数学计算:熟练使用模运算和数字处理
  4. 错误处理:深入理解了Result类型处理错误情况
  5. 性能优化:了解了不同实现方式的性能特点
  6. 边界情况处理:学会了处理各种输入边界情况

这些技能在实际开发中非常有用,特别是在处理数据验证、表单处理、支付系统等场景中。Luhn算法虽然是一个具体的校验算法,但它涉及到了字符串处理、算法实现、错误处理等许多核心概念,是学习Rust实用编程的良好起点。

通过这个练习,我们也看到了Rust在数据验证和算法实现方面的强大能力,以及如何用安全且高效的方式实现经典算法。这种结合了安全性和性能的语言特性正是Rust的魅力所在。

http://www.dtcms.com/a/581983.html

相关文章:

  • 远程调用基本实现
  • 上海微网站制作建设网页设计心得300
  • 网站视频与服务器的关系html网页制作颜色代码
  • 北京亦庄信创产业链闭环初成,下一站是“生态质量”
  • 【uniapp】小程序体积优化,分包异步化
  • 如何做出好的产品:黑客马拉松产品核心逻辑[特殊字符]
  • 网站注入木马crm管理系统登录
  • Vue 2 和 Vue 3 的区别
  • 【FPGA】使用移位实现LED流水灯
  • Arbess零基础学习 - 使用Arbess+GitLab+Hadess实现Java项目自动化构建/主机部署/上传制品
  • S12 简单排序算法--冒泡 选择 直接插入 希尔排序
  • 【RabbitMQ】工作模式实现
  • 自己做淘宝优惠券网站旅游网站模板html免费下载
  • 进一步强化网站建设wordpress用户权限在哪改
  • 【Android Studio】Android Studio的安装过程以及初步使用
  • [人工智能-大模型-138]:如何把文本语言转化成词向量,然后作为模型的输入?给出中间每个步骤的输入和输出的实例值。
  • [Linux]学习笔记系列 -- [kernel]cpu
  • 河南建设工程信息网站怎么自己做游戏软件的app
  • 公司做网站是做什么账务处理传奇网页版在线玩
  • 西门子PLC扩展模块连接兼容性详解
  • 手机使用过的痕迹能查到吗?完整查询指南与步骤
  • Python 类继承详解:深度学习神经网络架构的构建艺术
  • 网站域名申请怎么做wordpress七牛云图床插件
  • 界面控件DevExpress WPF v25.1新版亮点:数据管理功能全新升级
  • 张家港专业的网站制作公司建设部职称网站
  • C# AutoResetEvent和ManualResetEvent
  • AI驱动半导体良率提升:基于机器学习的晶圆缺陷分类系统搭建
  • 系统架构师·案例分析相关知识点
  • 建站公司前景网站运营有前途吗
  • Leetcode 48