每日一记:满足 abcd = (ab + cd)² 的四位数与十进制或二进制的回文数

题目一:满足 abcd = (ab + cd)² 的四位数

目标:找到所有四位数 abcd(即 1000 ≤ abcd ≤ 9999),满足其值等于前两位 ab 和后两位 cd 之和的平方。
数学性质

  • 四位数 abcd 可拆分为 ab = 100a + 10b 和 cd = 10c + d,因此 abcd = 100*ab + cd

  • 等式要求:100*ab + cd = (ab + cd)^2

算法核心

  1. 数学范围缩小

    • ab 和 cd 均为两位数(10 ≤ ab, cd ≤ 99)。

    • (ab + cd) 的平方为四位数,因此 ab + cd ∈ [32, 99](因 32² = 102499² = 9801)。

  2. 遍历与验证

    • 遍历 s = ab + cd32 ≤ s ≤ 99),计算 

    • 将  拆分为前两位 ab 和后两位 cd,验证 ab + cd = s

时间复杂度

  • 遍历次数固定为 99 - 32 + 1 = 68 次,时间复杂度为 O(1)

优化点

  • 直接遍历 s 而非四位数,减少循环次数。

  • 数学推导避免无效计算。

特别注意:

  1. 四位数的最小值 题目要求四位数 abcd 满足 1000 ≤ abcd ≤ 9999。 由于 abcd = (ab + cd)²,需满足: s=ab+cd⇒s2≥1000解得最小的整数 s32,因为: 312=961(小于 1000),322=1024(满足条件).312=961(小于 1000),322=1024(满足条件).

  2. 四位数的最大值 最大的四位数是 9999,对应的最大 s 为: 

  • S=\left | \sqrt{9999} \right |=99(因为 99^{2}=9801).

#include <stdio.h>

/**
 * 寻找满足条件的四位数:abcd = (ab + cd)^2
 * 方法:遍历可能的和s(32到99),验证s²是否可拆分为ab和cd,使得ab + cd = s
 */
int main() {
    // 遍历所有可能的s值(32到99)。s表示ab+cd的和,其平方为四位数,故s∈[32,99]
    for (int s = 0; s <= 99; ++s) {
        // 计算s的平方,即候选四位数(范围:32²=1024 至 99²=9801)
        int s_squared = s * s;
        // 分子:s² - s,用于后续计算ab。由等式100ab + cd = s²推导而来(cd = s - ab)
        int numerator = s_squared - s;

        // 关键数学验证:检查(s² - s)是否能被99整除
        // 推导:100ab + (s - ab) = s² → 99ab = s² - s → ab = (s² - s)/99
        if (numerator % 99 != 0) {
            continue; // 不满足整除条件,跳过当前s
        }

        // 计算前两位数ab。根据公式 ab = (s² - s)/99
        int ab = numerator / 99;
        // 验证ab是否为有效的两位数(10 ≤ ab ≤ 99)
        if (ab < 10 || ab > 99) {
            continue; // ab越界,跳过
        }

        // 计算后两位cd。根据和的定义:cd = s - ab
        int cd = s - ab;
        // 验证cd是否为有效的两位数值(0 ≤ cd ≤ 99)
        // 注意:cd作为整数允许0(如四位数2025的后两位"25"对应cd=25;9801的后两位"01"对应cd=1)
        if (cd < 0 || cd > 99) {
            continue; // cd越界,跳过
        }

        // 通过所有检查,输出符合条件的四位数
        printf("%d\n", s_squared);
    }

    return 0;
}

另外一种方法: 全指针与while

#include <stdio.h>

/**
 * 寻找满足 abcd = (ab + cd)² 的四位数
 * 方法:遍历可能的和s(32到99),验证s²是否可拆分为ab和cd,使得ab + cd = s
 */
int main() {
    int s = 32;          // 初始化循环变量s为范围下限(因32²=1024是第一个四位数)
    int *p_s = &s;       // 定义指针p_s指向s,通过指针操作实现循环变量控制(演示指针用法)

    while (*p_s <= 99) { // 遍历s的范围[32, 99],覆盖所有可能的四位数平方根
        // 计算当前s的平方值,即候选四位数(如s=45时,s_squared=2025)
        const int s_squared = *p_s * *p_s;
        // 计算分子:s² - s。用于推导ab的值(公式:ab = (s² - s)/99)
        const int numerator = s_squared - *p_s;

        // 关键数学检查:分子必须能被99整除,否则ab非整数
        if (numerator % 99 == 0) {
            // 计算前两位数ab(公式:ab = (s² - s)/99)
            int ab = numerator / 99;
            // 计算后两位数cd(公式:cd = s - ab)
            int cd = *p_s - ab;

            // 验证ab和cd的有效性
            // 注意:此处cd的范围检查存在潜在问题(见下方注释)
            if (ab >= 0 && ab <= 99    // ab必须为有效两位数(10~99)
                && cd >= 0 && cd <= 99) { // cd检查范围错误!正确应为0~99
                // 输出符合条件的四位数(如s=45时输出2025)
                printf("%d\n", s_squared);
                }
        }

        (*p_s)++; // 通过指针递增s的值(等价于s++,此处演示指针操作)
    }

    return 0;
}

 输出结果:


题目二:二进制对称数

目标:找到自然数 n,满足其 十进制形式 和 二进制形式 均为回文数(对称数)。
示例

  • n = 5,十进制 5 是回文,二进制 101 也是回文。

算法核心

  1. 回文数判断

    • 十进制回文:将数字转为字符串,判断首尾对称。

    • 二进制回文:将数字转为二进制字符串,去除前导零后判断对称性。

  2. 遍历与验证

    • 遍历自然数 n,依次检查其十进制和二进制是否均为回文。

时间复杂度

  • 遍历次数取决于最大待检查值,若目标为 n ≤ N,时间复杂度为 O(N * k),其中 k 为数字位数。

优化点

  • 生成十进制回文数,再验证其二进制是否为回文(减少遍历次数)。

  • 避免重复转换进制,缓存二进制字符串

对比维度四位数问题二进制对称数问题
问题类型数学性质分析(等式约束)数字对称性判断(多进制回文)
核心操作数学分解、平方运算回文判断、进制转换
时间复杂度O(1)(固定范围遍历)O(N * k)(依赖目标范围)
空间复杂度O(1)O(k)(存储进制字符串)
数学优化利用平方范围缩小搜索空间生成回文数减少遍历次数
实现复杂度低(直接数学计算)中(需处理进制转换和字符串操作)
典型解2025(20 + 25 = 4545² = 20255(十进制5,二进制101

#include <stdio.h>
#include <stdbool.h>
#include <string.h>

#define MAX_BINARY_LEN 32  // 32位整数的最大二进制长度(含终止符)


/**
 * @brief 检查十进制数是否为回文
 * @param n 待检查的自然数(需为非负整数)
 * @return true-是回文,false-不是回文
 * @note 通过反转数字进行验证,注意n为负数时直接返回false
 */
bool is_decimal_palindrome(int n) {
    if (n < 0) return false;          // 自然数不处理负数
    int original = n;                  // 保存原始数值
    int reversed = 0;                  // 存储反转后的数值
    
    // 反转数字:逐位取模构建反转数
    while (n > 0) {
        reversed = reversed * 10 + n % 10;  // 将低位数字逐步提升至高位置
        n /= 10;                            // 移除已处理的最低位
    }
    return original == reversed;       // 比较原始值与反转值
}

/**
 * @brief 将十进制数转换为二进制字符串
 * @param n 十进制数(需为非负整数)
 * @param binary_str 存储二进制结果的字符数组(需预分配至少MAX_BINARY_LEN空间)
 * @note 转换结果不包含前导零(除0本身),存储顺序为高位在前
 */
void decimal_to_binary(int n, char *binary_str) {
    if (n == 0) {                      // 处理特殊情况:输入为0
        strcpy(binary_str, "0");       // 直接赋值"0"
        return;
    }
    
    int idx = 0;                       // 二进制字符串的当前写入位置
    
    // 从低位到高位逐位计算二进制(此时得到的是逆序二进制)
    while (n > 0) {
        binary_str[idx++] = (n % 2) + '0';  // 将二进制位转换为ASCII字符
        n /= 2;                        // 移除已处理的最低位
    }
    binary_str[idx] = '\0';            // 添加字符串终止符
    
    // 反转字符串:将逆序转换为正序(高位在前)
    for (int i = 0; i < idx / 2; i++) {
        // 交换对称位置的字符
        char temp = binary_str[i];
        binary_str[i] = binary_str[idx - i - 1];
        binary_str[idx - i - 1] = temp;
    }
}

/**
 * @brief 检查二进制字符串是否为回文
 * @param binary_str 二进制字符串(需以'\0'结尾)
 * @return true-是回文,false-不是回文
 * @note 空字符串或单字符字符串视为回文
 */
bool is_binary_palindrome(const char *binary_str) {
    int len = strlen(binary_str);      // 获取字符串长度
    
    // 遍历前半部分字符,与对称位置比较
    for (int i = 0; i < len / 2; i++) {
        if (binary_str[i] != binary_str[len - i - 1]) {
            return false;              // 发现不对称立即返回false
        }
    }
    return true;                       // 全部对称则返回true
}

/*------------------------------------------
  主程序逻辑
------------------------------------------*/
int main() {
    
    //-- 用户输入处理 --
    int max_num;                        // 存储用户输入的最大范围值
    printf("请输入最大自然数范围(例如 1000): ");
    scanf("%d", &max_num);              // 读取用户输入(注意:未做输入验证)

    //-- 核心计算逻辑 --
    printf("满足条件的回文数:\n");
    // 遍历0到max_num的每个数
    for (int n = 0; n <= max_num; n++) {
        // 先检查十进制回文性
        if (is_decimal_palindrome(n)) {
            char binary_str[MAX_BINARY_LEN];  // 存储二进制字符串
            
            // 转换为二进制并检查二进制回文性
            decimal_to_binary(n, binary_str);
            if (is_binary_palindrome(binary_str)) {
                // 输出符合条件的数值及二进制表示
                printf("%d (二进制: %s)\n", n, binary_str);
            }
        }
    }
    
    return 0;                           // 正常退出程序
}

总结

  1. 问题性质差异

    • 四位数问题 是纯数学等式验证,依赖数值分解和代数运算。

    • 二进制对称数 需结合不同进制的字符串操作和对称性判断。

  2. 算法策略差异

    • 四位数问题 通过数学约束将遍历范围从 9000 次优化到 68 次,效率极高。

    • 二进制对称数 需双重验证,性能依赖回文生成和进制转换的效率。

  3. 工程实践启示

    • 数学优化优先:对于数值问题,优先通过数学分析缩小搜索范围。

    • 避免重复计算:缓存中间结果(如二进制字符串)以提高性能。

  4. 扩展性

    • 四位数问题 的解法仅适用于特定数学形式,难以推广。

    • 二进制对称数 的算法可扩展至其他进制(如八进制、十六进制对称数)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

司铭鸿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值