【无标题】

欧拉函数

5. 示例代码(Java)​

以下是使用质因数分解法计算欧拉函数的代码:

import java.util.HashSet;
import java.util.Set;

public class EulerTotientFunction {

    // 质因数分解并返回不同的质因数集合
    public static Set<Integer> primeFactors(int n) {
        Set<Integer> factors = new HashSet<>();
        while (n % 2 == 0) {
            factors.add(2);
            n /= 2;
        }
        for (int i = 3; i * i <= n; i += 2) {
            while (n % i == 0) {
                factors.add(i);
                n /= i;
            }
        }
        if (n > 1) {
            factors.add(n);
        }
        return factors;
    }

    // 计算欧拉函数 φ(n)
    public static int eulerTotient(int n) {
        if (n == 0) return 0;
        if (n == 1) return 1;

        int result = n;
        Set<Integer> factors = primeFactors(n);

        for (int p : factors) {
            result = result / p * (p - 1);
        }

        return result;
    }

    // 测试程序
    public static void main(String[] args) {
        for (int i = 1; i <= 20; i++) {
            System.out.println("φ(" + i + ") = " + eulerTotient(i));
        }
    }
}

以下是使用递归分解法计算欧拉函数的代码:

public class EulerTotient {
    // 计算phi(n)
    public static int phi(int n) {
        if (n == 1) return 1;
        int result = n;
        
        // 处理质因数2
        if (n % 2 == 0) {
            result -= result / 2;  // 等价于 result * (1-1/2)
            while (n % 2 == 0) n /= 2;
        }
        
        // 处理奇质因数
        for (int i = 3; i * i <= n; i += 2) {
            if (n % i == 0) {
                result -= result / i;  // 等价于 result * (1-1/i)
                while (n % i == 0) n /= i;
            }
        }
        
        // 若n仍有大于sqrt(n)的质因数
        if (n > 1) {
            result -= result / n;
        }
        
        return result;
    }
    
    public static void main(String[] args) {
        System.out.println("phi(4) = " + phi(4));  // 输出2
        System.out.println("phi(6) = " + phi(6));  // 输出2
    }
}

阶乘的和


import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
    	//本题需要找到合并阶乘的规律
    	//如果题目中没有A[i]阶乘加和的条件,那么m就是数组A中的最小值
    	//而如果要对A[i]的阶乘加和,可能出现两个较小的数的阶乘合并成一个较大数的阶乘的现象
    	//如3个2的阶乘可以合并成3的阶乘,4个3的阶乘可以合并成4的阶乘
    	//故得到规律:若数字num出现了cnt次,且num可以被(cnt+1)整除,那么就可以合并成(num+1)的阶乘
//    	所以本题就是要让m从nums[]中最小的元素开始,不断验证(每次按m+1验证)这个min可不可以合并,可以就合并
    	
        // 下面举例说明为什么要将nums[]中最小的元素作为因子初始值
        // 3! 6
        // 4! 24
        // 5! 120
        // 和为150 必是 3!的倍数! 因为 4!5!都是3!的倍数,所以加起来肯定也是 (min) 3!的倍数
        // 所以应该找出数组最小值作为初始的sum,再一个一个往上找
        // 比如上面这个例子,需要有 4个3!才能进化为 4!,由于只有一个 3!所以不能往上找了,break退出,答案就是3
//    	如果是nums的元素为3 3 3 3 4 5,则将3合并,结果为4 4 5
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        long[] nums = new long[n];
        long min = Long.MAX_VALUE;
        for (int i = 0; i < nums.length; i++) {
            nums[i] = sc.nextLong();
            min = Math.min(min, nums[i]);
        }
        long sum = min;  // sum为nums[]中的最小值
        long num = 0;    // num为nums[]中值为sum的数量
//        开始合成
//        18个8!可以合成2个9!
        while (true) {
            // 如下文注释:num在上个循环中是18,sum 在上个循环中 8更新为9
            // 根据 18 * 8! = 2 * 9 * 8! = 2 * 9!可知此次循环的 num 要更新为 18 / 9 = 2
//        	说明18 * 8!这个数的因数不但8!可以,9!也可以,因为求max因数,所以取9!
//        	所以解法:
//        	从nums[]中最小的元素作为因子初始值开始,找num*sum的组合,不断向上找可以符合这个组合的更大的sum,
//        	找到第一个sum+1不符合,则sum即为所求
        	
            num = num / sum;
//            num不但要算minNum合并后新num的个数,还要加上nums原本还剩几个新num,加一块才是新num在合并后在nums里的个数
            for (int i = 0; i < nums.length; i++) {
                if (nums[i] == sum) {
                    num++;
                }
            }
            // 如果sum的数量(num)等于sum+1的倍数,则可以将其逻辑上将这num个sum合成为(sum+1)的阶乘。
            // 形象化: sum = 2  num = 3  3个2 = 2+2+2    sum+1 = 3  3 = 3的倍数, 3个2合成为3的阶乘
            // 18(num)个8!(sum) = 18 * 8! = 2 * 9 * 8! = 2 * 9!  sum的数量18 = sum+1=9 的倍数
            if (num % (sum + 1) == 0 && num != 0) {
//            	说明还有更大的因数能与num分配成符合题意的组合,那么继续更新更大的因数
                sum++;
            } else break;
        }
        System.out.println(sum);
    }
}

公因数匹配

思路引用:冯勒布

//本题的思路比较巧妙,利用了分解质因子和查找质因子的思想
//对于输入的每个元素,都拆解其质因子,若该质因子是首次出现则存入map,记录其出现的位置
//若该质因子不是首次出现,说明在该元素的前面已经有元素具有共同的质因子,找到了一对下标(i,j)
//由于题目要求i和j都尽可能地小,因此要不断比较出最小的(i,j)再输出
//附:本题若暴力求解可使用gcd函数求最大公因数,可立即通过4个测试点 
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int n = scan.nextInt();
        int[] arr = new int[n];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = scan.nextInt();
        }

        Map<Integer, Integer> map = new HashMap<>();
        int start = -1, end = -1;

        for (int j = 0; j < arr.length; j++) {
            int temp = arr[j];
            int i = 2;
            while (i * i <= temp) {
                if (temp % i == 0) {
                    if (!map.containsKey(i)) {
                        map.put(i, j);
                    } else {
                        // 只有当当前 pair 更优时才更新
                        int prevIndex = map.get(i);
                        if (start == -1 || 
                           (prevIndex < start) || 
                           (prevIndex == start && j < end)) {
                            start = prevIndex;
                            end = j;
                        }
                    }
                    temp /= i;
                    i = 2;
                } else {
                    i++;
                }
            }

            // 处理剩余的大于1的部分(也是一个质因子)
            if (temp > 1) {
                if (!map.containsKey(temp)) {
                    map.put(temp, j);
                } else {
                    int prevIndex = map.get(temp);
                    if (start == -1 || 
                       (prevIndex < start) || 
                       (prevIndex == start && j < end)) {
                        start = prevIndex;
                        end = j;
                    }
                }
            }
        }

        if (end != -1) {
            System.out.println((start + 1) + " " + (end + 1));
        }
        scan.close();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值