存在交换半径的排列问题

有甲、乙、丙、丁、戊、己、庚、辛...等 n 个人按顺序坐在一列椅子上,现在每个人都可以交换位置(不一定要交换),但交换半径只能为m。问一共有几种交换方法?(m << n)

说明:如果m为2,则丙只能坐到甲乙丁戊的位置,而丁只能坐到乙丙戊己的位置。



背景:

这几个礼拜的Java每周作业都是破解字符替换式密码,如破解:53$$+305))6*;4826)4$.)4$);806*;48+8%60))85;;]8*;:$*8+83(88)5*+;46(;88*96*?;8)*$(;485);5*+2:*$(;4956*2(5*-4)8%8*;4069285);)6+8)4$$;1($9;48081;8:8$1;48+85;4)485+528806*81($9;48;(88;4($?34;48)4$;161;:188;$?;

一种方法就是字母频率分析,还有一种是单词模式分析。

在做字母频率分析的时候,分析完了密码的字符频率和当代的英文书籍的字母频率后不知道怎么做,因为它们不能按照频率大小顺序简单地对应起来。但最大频率的字符肯定不会对应到字母"x"或"z"这种生僻字母,只会对应字母频率的第一大,第二大,第三大或第四大,越往后概率越低,直到变成小概率事件。

这里就想到了如果对应关系在一定的半径之内,列出所有的可能再进行下一步操作。

这就引出了一个问题上面的问题。



脑子热写了一个递归,没有严格证明,简单证明就是如果设置n = m,那么结果就是n个元素的全排列的所有情况的数目。


示例代码(用javadoc写的解释):

package testLand;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
/**
 * @author Serious
 * @version 1.0
 * 
 * Sample input and output
 * 
 * n: 14 m: 2
 * answer: 64481
 * num_of_operations: 582470767
 * time: 123s
 *
 * n: 13 m: 2
 * answer: 27495
 * num_of_operations: 116486835
 * time: 22s
 * 
 * n: 12 m: 2
 * answer: 11854
 * num_of_operations: 23297053
 * time: 4s
 *
 *n: 11 m: 2
 * answer: 5080
 * num_of_operations: 4665756
 * time: 1s
 */
public class test {  
	private static int radius;
	private static int count = 0;
	private static HashMap<Character, Integer> hm = new HashMap<Character, Integer>();
	private static HashSet<ArrayList<Character>> set = new HashSet<ArrayList<Character>>(); 
	public static void setR(int i) {
		radius = i;
	}
	public static void main(String[] args) {
		long begin = System.currentTimeMillis();
		setR(2);
		char[] testArr = {'a','b','c','d','e','f','g','h','i','j','k','l','m'};
		System.out.println("n: "+testArr.length+" m: "+radius);
		for(int i = 0 ; i <testArr.length ; i++) 
			hm.put(testArr[i], i);
		ans(0, testArr);
		System.out.println("answer: "+set.size());
		System.out.println("num_of_operations: "+count);
		long end = System.currentTimeMillis();
		System.out.println("time: "+(end-begin)/1000+"s");
	}
	/**
	 * This method is for searching all combinations corresponding to the question 
	 * Check all the periphery of the accessible area of the element——testArr[index], if 
	 * the element in checking point is able to exchange, exchange testArr[index] and 
	 * testArr[check]. After exchanging, put the new combination into a HashSet which can
	 * remove the duplicate items, and then, the recursion come to next layer by invoking
	 * the method itse: if(index!=testArr.length-1) ans(index+1, testArr)
	 * <P>
	 * @param index        The index of present checking element in array
	 * @param testArr      An array for searching the answer 
	 */
	public static void ans(int index, char[] testArr) {
		
		for(int check = index - radius; check <= index + radius; check++ ) {
			if(check>-1 && check < testArr.length-1) {
				if(check != index && hm.get(testArr[check])-radius <=index && index<=hm.get(testArr[check]) +radius 
						&& hm.get(testArr[index])-radius<=check && check<=hm.get(testArr[index])+radius) {	
					testArr = switchE(index, check, testArr);
					char[] tmp = testArr;
					for(int n = 0 ; n < testArr.length ; n++) {
						char tmpc = testArr[n];
						tmp[n] = tmpc;
					}
					ArrayList<Character> tmpArr = new ArrayList<Character>();
						for(char c : testArr)
							tmpArr.add(c);
					count++;
					set.add(tmpArr);
				}
				if(index != testArr.length-1) ans(index+1, testArr);
			}
		}
	}
	public static char[] switchE(int i, int j, char[] testArr) {
		char temp = testArr[i];
		char temp2 = testArr[j];
		testArr[i] = temp2 ;
		testArr[j] = temp;
		return testArr;
	}
}


进步的想法就是,先列出全排列,再逐一筛选符合要求的。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
```c #include <stdio.h> #include <stdlib.h> #include <math.h> #include <stdbool.h> #define MAX_N 10 // 圆结构体 typedef struct Circle { int r; // 半径 int x; // 圆心横坐标 int y; // 圆心纵坐标 } Circle; // 矩形结构体 typedef struct Rectangle { int w; // 矩形宽度 int h; // 矩形高度 } Rectangle; // 计算两个圆的切点 bool get_tangent_points(Circle c1, Circle c2, Circle* p1, Circle* p2) { int d = sqrt(pow(c2.x - c1.x, 2) + pow(c2.y - c1.y, 2)); // 圆心距离 if (d < c1.r + c2.r) { // 两圆相交 return false; } double sin_alpha = (double)(c2.y - c1.y) / d; // sin alpha double cos_alpha = (double)(c2.x - c1.x) / d; // cos alpha double sin_beta = (double)c1.r / d; // sin beta double cos_beta = (double)c2.r / d; // cos beta double sin_theta = sin_alpha * cos_beta - cos_alpha * sin_beta; // sin theta double cos_theta = cos_alpha * cos_beta + sin_alpha * sin_beta; // cos theta p1->x = c1.x + c1.r * cos_theta; p1->y = c1.y + c1.r * sin_theta; p2->x = c2.x - c2.r * cos_theta; p2->y = c2.y - c2.r * sin_theta; return true; } // 计算圆与矩形的交点 bool get_intersection_point(Circle c, Rectangle r, Circle* p1, Circle* p2) { if (c.x - c.r < 0 || c.x + c.r > r.w || c.y - c.r < 0) { // 圆超出矩形 return false; } p1->x = c.x; p1->y = c.y; p2->x = c.x; p2->y = r.h; return true; } // 计算排列长度 double get_permutation_length(Circle* circles, int n) { double length = 0; for (int i = 1; i < n; i++) { Circle p1, p2; bool has_tangent_points = false; for (int j = i - 1; j >= 0; j--) { Circle t1, t2, p3, p4; if (get_tangent_points(circles[i], circles[j], &t1, &t2)) { if (!has_tangent_points) { p1 = t1; p2 = t2; has_tangent_points = true; } else { double d1 = sqrt(pow(t1.x - p2.x, 2) + pow(t1.y - p2.y, 2)); double d2 = sqrt(pow(t2.x - p1.x, 2) + pow(t2.y - p1.y, 2)); if (d1 < d2) { p1 = t1; p2 = p2; } else { p1 = p1; p2 = t2; } } } if (get_intersection_point(circles[i], (Rectangle){p2.x - p1.x, p2.y}, &p3, &p4)) { if (!has_tangent_points) { p1 = p3; p2 = p4; has_tangent_points = true; } else { double d1 = sqrt(pow(p3.x - p2.x, 2) + pow(p3.y - p2.y, 2)); double d2 = sqrt(pow(p4.x - p1.x, 2) + pow(p4.y - p1.y, 2)); if (d1 < d2) { p1 = p3; p2 = p2; } else { p1 = p1; p2 = p4; } } } } length += sqrt(pow(p2.x - p1.x, 2) + pow(p2.y - p1.y, 2)) + 2 * circles[i].r; } return length; } // 交换两个圆 void swap(Circle* c1, Circle* c2) { Circle tmp = *c1; *c1 = *c2; *c2 = tmp; } // 全排列 void permutation(Circle* circles, int n, int i, double* min_length, Circle* min_circles) { if (i == n) { double length = get_permutation_length(circles, n); if (length < *min_length) { *min_length = length; for (int j = 0; j < n; j++) { min_circles[j] = circles[j]; } } } else { for (int j = i; j < n; j++) { swap(&circles[i], &circles[j]); permutation(circles, n, i + 1, min_length, min_circles); swap(&circles[i], &circles[j]); } } } int main() { int n; scanf("%d", &n); Circle circles[MAX_N]; for (int i = 0; i < n; i++) { scanf("%d", &circles[i].r); circles[i].x = 0; circles[i].y = circles[i].r; } double min_length = INFINITY; Circle min_circles[MAX_N]; permutation(circles, n, 0, &min_length, min_circles); printf("%.2f ", min_length); for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { if (circles[j].r == min_circles[i].r) { printf("%d ", j + 1); break; } } } putchar(' '); return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值