前言
撰写此博客,也是源于一道离散数学题。求1到1000之间(包含1和1000在内)既不能被5和6,也不能被8整除的数的个数。在第一次写的时候,用Java很简单的实现了上述讲到的数学问题。可在实际应用中,我们利用包含排斥原理来筛选数据,这些数据和筛选的规则往往不是固定的,所以,突发奇想:能不能利用包含排斥原理求x~y之间不能被x1,x2,x3.....xn整除的个数?本文为了让效果更明显,也为了本人温习下Android,所以用Android xml绘制界面和Java代码实现了这一过程。 so,阅读本文章的你需要有一点Android基础了。
效果预览
那我们来看一看是如何实现的吧!
需求与分析
需求:
利用包含排斥原理计算x~y之间不能被x1,x2,x3.....xn整除的个数
包含排斥原理:
分析:
步骤:
- 先求出x1,x2,x3.....xn(类型为List)的排列数(C(n,k))将其存储在map集合中;
- 遍历map集合中的key对应的list,对每个key对应的list中的元素求最小公倍数;
- x~y之间不能被x1,x2,x3......整除的个数:
利用包含排斥原理:
个数=区间长度(-/+,集合中每个key对应的list中的元素个数为奇数是为-,偶数为+)区间长度除以集合中每个key对应的list中的元素求最小公倍数个数求和得到的个数;
实现
界面实现
界面的代码很简单就是简单的相对布局
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="16dp"
android:background="@drawable/bg"
tools:context="com.example.huerfeng.counter.MainActivity">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#d5ecfa"
android:textAlignment="center"
android:padding="15dp"
android:layout_margin="10dp"
android:text="@string/t_title"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/t_start_num"
android:id="@+id/tv_startNum"
android:layout_below="@+id/title"
android:layout_alignStart="@+id/title"
android:layout_marginTop="44dp"
android:paddingBottom="5dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/t_end_num"
android:id="@+id/tv_endNum"
android:layout_below="@+id/tv_startNum"
android:layout_alignStart="@+id/tv_startNum"
android:layout_marginTop="56dp"
android:paddingBottom="5dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/t_divisor"
android:id="@+id/tv_divisor"
android:layout_centerVertical="true"
android:layout_alignStart="@+id/tv_endNum" />
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/t_result_view"
android:id="@+id/tv_calc"
android:layout_marginTop="121dp"
android:layout_below="@+id/tv_divisor"
android:layout_centerHorizontal="true"
android:textAlignment="center"/>
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/et_startNum"
android:layout_above="@+id/tv_endNum"
android:layout_alignEnd="@+id/title"
android:textAlignment="center"
android:layout_toEndOf="@+id/tv_calc"
android:layout_alignStart="@+id/tv_calc"
android:inputType="number"/>
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAlignment="center"
android:id="@+id/et_endNum"
android:layout_alignBottom="@+id/tv_endNum"
android:layout_alignStart="@+id/et_startNum"
android:layout_alignEnd="@+id/et_startNum"
android:inputType="number"/>
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAlignment="center"
android:id="@+id/et_divisor"
android:layout_centerVertical="true"
android:layout_alignStart="@+id/et_endNum"
android:layout_alignEnd="@+id/title"
/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/bt_calc"
android:text="@string/t_calc"
android:background="#aae2df"
android:layout_below="@+id/et_divisor"
android:layout_margin="30dp"
android:layout_centerHorizontal="true"
android:layout_marginTop="41dp" />
</RelativeLayout>
功能实现
代码如下:
import android.util.Log;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Counter {
// 区间长度
int intervalLen = 0;
int count = 0;
Map<Integer, List> map = new HashMap<>();
int key = 0;
// 求两个数的最大公约数
public int getGcd(int a, int b) {
if (b == 0)
return a;
return getGcd(b, a % b);
}
// 求两个数的最小公倍数
public int getLcm(int a, int b) {
return (a * b) / getGcd(a, b);
}
// 求多个数的最小公倍数
public int getMoreLcm(List num, int n) {
if (n == 1)
return (int) num.get(n - 1);
return getLcm((int) num.get(n - 1), getMoreLcm(num, n - 1));
}
/**
* 步骤::每次递归时,把原始数据和满足条件的工作空间复制一份,所有的操作均在复制文件中进行,目的就是保证不破坏原始数据,
* 从而可以让一轮递归结束后可以正常进行下一轮。
* 其次,把数据的第一个元素添加到工作空间中,判断工作空间的大小,如果小于k,则需要继续递归,但此时,传入递归函数的
* 参数需要注意:假设当前插入的节点的下标是i,因为是顺序插入的,所以i之前的所有数据都应该舍去,只传入i之后的未使用过的数据。
* 因此在传参之前,应该对copydata作以处理;当大于k的时候,则表明已经找到满足条件的第一种情况,然后只需修改该情况的最后一个结果即可。
* 如:找到abc时,则只需替换c为d即可完成该轮递归。
*
* @param data
* 原始数据
* @param workSpace
* 自定义一个临时空间,用来存储每次符合条件的值
* @param k
* C(n,k)中的k
*/
public <E> void combinerSelect(List<E> data, List<E> workSpace, int n, int k) {
List<E> copyData;
List<E> copyWorkSpace;
List<E> list = new ArrayList<>();
if (workSpace.size() == k) {
key++;
for (E c : workSpace) {
list.add(c);
}
map.put(key, list);
}
for (int i = 0; i < data.size(); i++) {
copyData = new ArrayList<E>(data);
copyWorkSpace = new ArrayList<E>(workSpace);
copyWorkSpace.add(copyData.get(i));
for (int j = i; j >= 0; j--)
copyData.remove(j);
combinerSelect(copyData, copyWorkSpace, n, k);
}
}
public int getCount(int startNum, int endNum, List<Integer> data) {
intervalLen=endNum-startNum+1;
count=intervalLen;//初始长度为区间长度
for (int i = 1; i <= data.size(); i++) {
combinerSelect(data, new ArrayList<Integer>(), data.size(), i);
}
Log.e("x1,x2,x3.....xn的排列数",map.toString());
for (int i = 1; i <= map.size(); i++) {
if(map.get(i).size()%2==0){
//集合中每个key对应的list中的元素个数为偶数时为+
count+=intervalLen/getMoreLcm(map.get(i), map.get(i).size());
}else {
//集合中每个key对应的list中的元素个数为奇数时为-
count-=intervalLen/getMoreLcm(map.get(i), map.get(i).size());
}
}
return count;
}
}
我在这里创建了一个Counter类,按照需求分析的步骤,里面包含了几个方法:计算最小公倍数
和计算最小公倍数时需要用到的最大公约数方法,以及求全排列的方法。都是用了递归的思想,其中求全排列的方法是引用了另外一个博主的,当然部分有做修改,如果你想要了解更多关于排列的算法,请参照博客:https://blog.csdn.net/yhyr_ycy/article/details/52523243。最后一个是getCount方法,参照需求分析第三个步骤。关于方法的用处在这里不做过多的解释,在代码中注释都有详细的解释。如果你有任何不明白的地方,欢迎在下方评论交流哦。
界面交互的MainActivity类
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class MainActivity extends AppCompatActivity {
public static final String COMMA_NUMBER= "^[0-9]+([,][0-9]+)*$";
private EditText et_startNum;
private EditText et_endNum;
private EditText et_divisor;
private TextView tv_calc;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
et_startNum= (EditText) findViewById(R.id.et_startNum);
et_endNum= (EditText) findViewById(R.id.et_endNum);
et_divisor= (EditText) findViewById(R.id.et_divisor);
tv_calc= (TextView) findViewById(R.id.tv_calc);
Button bt_calc = (Button) findViewById(R.id.bt_calc);
bt_calc.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
List<Integer> data=getDivisorList(et_divisor.getText().toString());
String startNum=et_startNum.getText().toString();
String endNum=et_endNum.getText().toString();
checkDataAndCalc(startNum,endNum,data);
}
});
}
//检测输入框中数据并计算
public void checkDataAndCalc(String startNum, String endNum, List<Integer> data){
if(startNum.isEmpty()){
Toast.makeText(MainActivity.this,"开始数不能为空",Toast.LENGTH_SHORT).show();
}
else if(endNum.isEmpty()){
Toast.makeText(MainActivity.this,"结束数不能为空",Toast.LENGTH_SHORT).show();
}
else if (Integer.parseInt(startNum)>Integer.parseInt(endNum)){
Toast.makeText(MainActivity.this,"开始数不能大于结束数",Toast.LENGTH_SHORT).show();
}
else {
if(data!=null){
tv_calc.setText(String.valueOf(new Counter().getCount(Integer.parseInt(startNum),Integer.parseInt(endNum),data)));
}
}
}
public List<Integer> getDivisorList(String str){
String str2=str.replace(" ", "");//去掉用户可能输入的空格
if(str2.matches(COMMA_NUMBER)){//去掉空格后,对除数的输入格式进行正则匹配,只允许包含,和数字
List<Integer> divisorList=new ArrayList<>();
List<String> list= Arrays.asList(str2.split(","));
Log.e("list",list.toString());
for (String i:list) {
divisorList.add(Integer.parseInt(i));
}
return divisorList;
}else {
Toast.makeText(this,"除数请严格按1,2,3.." +"的格式输入",Toast.LENGTH_SHORT).show();
}
return null;
}
}
Activity和AppCompatActivity都是Android的核心类,在Activity类有onCreate事件方法,一般用于对Activity进行初始化,相当于程序的入口,并且通过setContentView方法将View(通过XML实现的视图等)放到Activity上,绑定后,Activity会显示View上的控件。在这里,我们一般都要对用户输入的数据进行检测,所以在这里写了一个checkDataAndCalc方法对用户输入的数据进行检测。对于开始数x和结束数y的限制输入而言,很简单,只需要对XML中的EditText控件中的inputType属性设置输入类型就ok了
android:inputType="number"/>
public static final String COMMA_NUMBER= "^[0-9]+([,][0-9]+)*$";
String str2=str.replace(" ", "");//去掉用户可能输入的空格
if(str2.matches(COMMA_NUMBER)){//去掉空格后,对除数的输入格式进行正则匹配,只允许包含,和数字
![吐舌头](https://static-blog.csdn.net/xheditor/xheditor_emot/default/tongue.gif)
总结
在处理一些问题的时候, 对于人来说,处理起来耗时费力,所以交给计算机处理就ok了。通过对包含排斥原理的运用,我们可以处理一些数学逻辑上的运算来达到减少运算次数的目的,在实际生活中,我们可以利用它对数据进行筛选。其实我们在实现的时候,都是一些算法的组合应用,只是在这里并没有仔细研究效率的问题。我还是个未入门的菜鸟,如果有错误的地方,欢迎指正。如果你有任何不了解的地方或者有更好更快速的方法,欢迎在下方留言评论哦。
版权声明:本文为博主原创文章,部分内容为引用,已注明出处,所以希望你在转载的时候也记得注明出处哦。 https://blog.csdn.net/huerbo2/article/details/79948235