找出两个极限之间的所有 3 和 5 的倍数 - 复杂性
Find all multiples of 3 & 5 between two limits - Complexity
我正在尝试查找 1 到 10000000(包括两者)之间的所有数字。我尝试了两种解决方案
- 蛮力法:遍历从 1 到 10000000 的所有数字,找出所有可以被 3 或 5 或两者整除的数字。
- 分而治之的方法:有 4 个计数器(2 个从开始,2 个从结束)。 2 个计数器处理 3 的倍数,两个计数器处理 5 的倍数。我将所有倍数放在一个 Set 中(我不需要 Sorted 元素,我只需要 elements ,排序也会增加我的复杂性)。
但是,循环方法比 'Divide & Conquer approach' 花费的时间更短(大约少 10 倍)。
我也在网上搜索了解决方案。但是,我只能找到循环方法。我的方法中是否缺少某些东西会增加我的执行时间?请指出这一点。我从 List 开始,转到 Sorted Set,最后决定使用 HashSet,但似乎需要时间。
这是我试过的。
`
public static void main(String[] args) {
System.out.println("Numbers divisible by 3 and 5:");
nosDivisibleBy3And5(); // divide & conquer approach (approach to consider)
nosDivisibleBy3And5BruteForce();
}
private static void nosDivisibleBy3And5BruteForce() {
IntStream ar = IntStream.range(1, 10000001); // start inclusive, end exclusive
Integer[] array = ar.boxed().toArray(Integer[]::new);
List<Integer> list = new ArrayList<>();
int count = 0;
long start = System.currentTimeMillis();
/*
* Traversing array from 1 to 100,
* if it is either divisible by 3 or 5 or both, count it , print it.
*
*/
for(int i = 0; i < array.length ; i ++) {
if((array[i] % 3 == 0) || (array[i] % 5 == 0)) {
//System.out.println(array[i]);
list.add(array[i]);
count++;
}
}
long end = System.currentTimeMillis();
System.out.println("Brute Force Approach:");
System.out.println("No of elements counted: " + count);
//Collections.sort(list);
//System.out.println("Elements: " + list);
System.out.println("Time: " + (end - start));
}
private static void nosDivisibleBy3And5() {
/*
* Set has all those numbers which
* are divisible by both 3 and 5.
*
*/
Set<Integer> elementsSet = new HashSet<Integer>();
int fr3,
fr5,
mid,
count;
fr3 = 2; // fr3 indicates the index of the first value divisible by 3.
fr5 = 4; // fr5 indicates the index of the first value divisible by 5.
count = 0;
int end3 = 9999998 , // end3 indicates the index of the last value divisible by 3.
end5 = 9999999; // end5 indicates the index of the last value divisible by 5.
/* Getting all the numbers from 1 to 100 from Intstream object */
IntStream ar = IntStream.range(1, 10000001); // start inclusive, end exclusive
Integer[] array = ar.boxed().toArray(Integer[]::new);
/*
* Using divide and conquer approach , mid divides the array from 1 to 100
* in two parts, on the first fr3 and fr5 will work, on the second part end3
* and end5 will work.
*/
mid = (fr3 + end3)/2;
long start = System.currentTimeMillis();
while(fr3 <= mid && end3 >= mid) {
elementsSet.add(array[fr3]);
elementsSet.add(array[fr5]);
elementsSet.add(array[end3]);
elementsSet.add(array[end5]);
fr3 += 3;
fr5 += 5;
end3 -= 3;
end5 -= 5;
}
long end = System.currentTimeMillis();
System.out.println("Our approach");
System.out.println("No of elements counted: " + elementsSet.size());
//System.out.println("Elements:" + elementsSet);
System.out.println("Time: " + (end - start));
}
}
`
HashSet 在散列和检查元素是否已存在上花费大量时间,并且比裸 ArrayList 慢 add()
如果您的问题确实是找到所有可被 3 或 5 整除的数字,那么您可以使用预定长度的数组:
int from = 1;
int to = 1000000;
int d3 = (to / 3) - (from / 3) + (from % 3 == 0 ? 1 : 0); // how many divisible by 3
int d5 = (to / 5) - (from / 5) + (from % 5 == 0 ? 1 : 0); // how many divisible by 5
int d15 = (to / 15) - (from / 15) + (from % 15 == 0 ? 1 : 0); // how many divisible by 15
int[] array = new int[d3 + d5 - d15]; // counted 15's twice
int offset = 0;
for (int i = from; i <= to; i++) {
if (i % 3 == 0 || i % 5 == 0) array[offset++] = i;
}
如果返回 List<Integer>
是目标,您可以通过扩展 AbstractList
并实现 iterator()
来获得 O(1) 时间和 space 解决方案跟踪下一个数的索引,实现get(int index)
、size()
、equals()
、hashCode()
等迭代器的方法都是根据最大数计算实现的。
List 将是不可变的(只需使用 Collections. unmodifiableList()
包装),但会履行 List 的契约。
全部完成,实际上没有存储任何数字。
这是另一个解决方案,部分基于优秀的 。
它简化了计算数组大小的逻辑,但添加了更多代码以防止需要使用相对较慢的 %
余数运算符,并消除了对不进入的数字进行迭代的需要结果数组。
因此,它应该执行得更快,但我还没有进行基准测试以查看它是否确实如此。
static int[] multiplesOfThreeAndFive(int from, int to) { // both inclusive
int count = ((to / 3) - ((from - 1) / 3)) // how many divisible by 3
+ ((to / 5) - ((from - 1) / 5)) // how many divisible by 5
- ((to / 15) - ((from - 1) / 15)); // how many divisible by 15, counted twice above
int[] result = new int[count];
int[] multiples = { 0, 3, 5, 6, 9, 10, 12 };
int startIndex = Arrays.binarySearch(multiples, from % 15);
if (startIndex < 0)
startIndex = -startIndex - 1;
for (int r = 0, offset = from / 15 * 15; r < count; offset += 15, startIndex = 0)
for (int i = startIndex; r < count && i < multiples.length; i++, r++)
result[r] = offset + multiples[i];
return result;
}
测试
System.out.println(Arrays.toString(multiplesOfThreeAndFive(1, 100)));
System.out.println(Arrays.toString(multiplesOfThreeAndFive(0, 100)));
System.out.println(Arrays.toString(multiplesOfThreeAndFive(29, 100)));
System.out.println(Arrays.toString(multiplesOfThreeAndFive(30, 100)));
System.out.println(Arrays.toString(multiplesOfThreeAndFive(31, 99)));
System.out.println(Arrays.toString(multiplesOfThreeAndFive(31, 101)));
输出
[3, 5, 6, 9, 10, 12, 15, 18, 20, 21, 24, 25, 27, 30, 33, 35, 36, 39, 40, 42, 45, 48, 50, 51, 54, 55, 57, 60, 63, 65, 66, 69, 70, 72, 75, 78, 80, 81, 84, 85, 87, 90, 93, 95, 96, 99, 100]
[0, 3, 5, 6, 9, 10, 12, 15, 18, 20, 21, 24, 25, 27, 30, 33, 35, 36, 39, 40, 42, 45, 48, 50, 51, 54, 55, 57, 60, 63, 65, 66, 69, 70, 72, 75, 78, 80, 81, 84, 85, 87, 90, 93, 95, 96, 99]
[30, 33, 35, 36, 39, 40, 42, 45, 48, 50, 51, 54, 55, 57, 60, 63, 65, 66, 69, 70, 72, 75, 78, 80, 81, 84, 85, 87, 90, 93, 95, 96, 99, 100]
[30, 33, 35, 36, 39, 40, 42, 45, 48, 50, 51, 54, 55, 57, 60, 63, 65, 66, 69, 70, 72, 75, 78, 80, 81, 84, 85, 87, 90, 93, 95, 96, 99, 100]
[33, 35, 36, 39, 40, 42, 45, 48, 50, 51, 54, 55, 57, 60, 63, 65, 66, 69, 70, 72, 75, 78, 80, 81, 84, 85, 87, 90, 93, 95, 96, 99]
[33, 35, 36, 39, 40, 42, 45, 48, 50, 51, 54, 55, 57, 60, 63, 65, 66, 69, 70, 72, 75, 78, 80, 81, 84, 85, 87, 90, 93, 95, 96, 99, 100]
我正在尝试查找 1 到 10000000(包括两者)之间的所有数字。我尝试了两种解决方案
- 蛮力法:遍历从 1 到 10000000 的所有数字,找出所有可以被 3 或 5 或两者整除的数字。
- 分而治之的方法:有 4 个计数器(2 个从开始,2 个从结束)。 2 个计数器处理 3 的倍数,两个计数器处理 5 的倍数。我将所有倍数放在一个 Set 中(我不需要 Sorted 元素,我只需要 elements ,排序也会增加我的复杂性)。
但是,循环方法比 'Divide & Conquer approach' 花费的时间更短(大约少 10 倍)。 我也在网上搜索了解决方案。但是,我只能找到循环方法。我的方法中是否缺少某些东西会增加我的执行时间?请指出这一点。我从 List 开始,转到 Sorted Set,最后决定使用 HashSet,但似乎需要时间。
这是我试过的。
`
public static void main(String[] args) {
System.out.println("Numbers divisible by 3 and 5:");
nosDivisibleBy3And5(); // divide & conquer approach (approach to consider)
nosDivisibleBy3And5BruteForce();
}
private static void nosDivisibleBy3And5BruteForce() {
IntStream ar = IntStream.range(1, 10000001); // start inclusive, end exclusive
Integer[] array = ar.boxed().toArray(Integer[]::new);
List<Integer> list = new ArrayList<>();
int count = 0;
long start = System.currentTimeMillis();
/*
* Traversing array from 1 to 100,
* if it is either divisible by 3 or 5 or both, count it , print it.
*
*/
for(int i = 0; i < array.length ; i ++) {
if((array[i] % 3 == 0) || (array[i] % 5 == 0)) {
//System.out.println(array[i]);
list.add(array[i]);
count++;
}
}
long end = System.currentTimeMillis();
System.out.println("Brute Force Approach:");
System.out.println("No of elements counted: " + count);
//Collections.sort(list);
//System.out.println("Elements: " + list);
System.out.println("Time: " + (end - start));
}
private static void nosDivisibleBy3And5() {
/*
* Set has all those numbers which
* are divisible by both 3 and 5.
*
*/
Set<Integer> elementsSet = new HashSet<Integer>();
int fr3,
fr5,
mid,
count;
fr3 = 2; // fr3 indicates the index of the first value divisible by 3.
fr5 = 4; // fr5 indicates the index of the first value divisible by 5.
count = 0;
int end3 = 9999998 , // end3 indicates the index of the last value divisible by 3.
end5 = 9999999; // end5 indicates the index of the last value divisible by 5.
/* Getting all the numbers from 1 to 100 from Intstream object */
IntStream ar = IntStream.range(1, 10000001); // start inclusive, end exclusive
Integer[] array = ar.boxed().toArray(Integer[]::new);
/*
* Using divide and conquer approach , mid divides the array from 1 to 100
* in two parts, on the first fr3 and fr5 will work, on the second part end3
* and end5 will work.
*/
mid = (fr3 + end3)/2;
long start = System.currentTimeMillis();
while(fr3 <= mid && end3 >= mid) {
elementsSet.add(array[fr3]);
elementsSet.add(array[fr5]);
elementsSet.add(array[end3]);
elementsSet.add(array[end5]);
fr3 += 3;
fr5 += 5;
end3 -= 3;
end5 -= 5;
}
long end = System.currentTimeMillis();
System.out.println("Our approach");
System.out.println("No of elements counted: " + elementsSet.size());
//System.out.println("Elements:" + elementsSet);
System.out.println("Time: " + (end - start));
}
}
`
HashSet 在散列和检查元素是否已存在上花费大量时间,并且比裸 ArrayList 慢 add()
如果您的问题确实是找到所有可被 3 或 5 整除的数字,那么您可以使用预定长度的数组:
int from = 1;
int to = 1000000;
int d3 = (to / 3) - (from / 3) + (from % 3 == 0 ? 1 : 0); // how many divisible by 3
int d5 = (to / 5) - (from / 5) + (from % 5 == 0 ? 1 : 0); // how many divisible by 5
int d15 = (to / 15) - (from / 15) + (from % 15 == 0 ? 1 : 0); // how many divisible by 15
int[] array = new int[d3 + d5 - d15]; // counted 15's twice
int offset = 0;
for (int i = from; i <= to; i++) {
if (i % 3 == 0 || i % 5 == 0) array[offset++] = i;
}
如果返回 List<Integer>
是目标,您可以通过扩展 AbstractList
并实现 iterator()
来获得 O(1) 时间和 space 解决方案跟踪下一个数的索引,实现get(int index)
、size()
、equals()
、hashCode()
等迭代器的方法都是根据最大数计算实现的。
List 将是不可变的(只需使用 Collections. unmodifiableList()
包装),但会履行 List 的契约。
全部完成,实际上没有存储任何数字。
这是另一个解决方案,部分基于优秀的
它简化了计算数组大小的逻辑,但添加了更多代码以防止需要使用相对较慢的 %
余数运算符,并消除了对不进入的数字进行迭代的需要结果数组。
因此,它应该执行得更快,但我还没有进行基准测试以查看它是否确实如此。
static int[] multiplesOfThreeAndFive(int from, int to) { // both inclusive
int count = ((to / 3) - ((from - 1) / 3)) // how many divisible by 3
+ ((to / 5) - ((from - 1) / 5)) // how many divisible by 5
- ((to / 15) - ((from - 1) / 15)); // how many divisible by 15, counted twice above
int[] result = new int[count];
int[] multiples = { 0, 3, 5, 6, 9, 10, 12 };
int startIndex = Arrays.binarySearch(multiples, from % 15);
if (startIndex < 0)
startIndex = -startIndex - 1;
for (int r = 0, offset = from / 15 * 15; r < count; offset += 15, startIndex = 0)
for (int i = startIndex; r < count && i < multiples.length; i++, r++)
result[r] = offset + multiples[i];
return result;
}
测试
System.out.println(Arrays.toString(multiplesOfThreeAndFive(1, 100)));
System.out.println(Arrays.toString(multiplesOfThreeAndFive(0, 100)));
System.out.println(Arrays.toString(multiplesOfThreeAndFive(29, 100)));
System.out.println(Arrays.toString(multiplesOfThreeAndFive(30, 100)));
System.out.println(Arrays.toString(multiplesOfThreeAndFive(31, 99)));
System.out.println(Arrays.toString(multiplesOfThreeAndFive(31, 101)));
输出
[3, 5, 6, 9, 10, 12, 15, 18, 20, 21, 24, 25, 27, 30, 33, 35, 36, 39, 40, 42, 45, 48, 50, 51, 54, 55, 57, 60, 63, 65, 66, 69, 70, 72, 75, 78, 80, 81, 84, 85, 87, 90, 93, 95, 96, 99, 100]
[0, 3, 5, 6, 9, 10, 12, 15, 18, 20, 21, 24, 25, 27, 30, 33, 35, 36, 39, 40, 42, 45, 48, 50, 51, 54, 55, 57, 60, 63, 65, 66, 69, 70, 72, 75, 78, 80, 81, 84, 85, 87, 90, 93, 95, 96, 99]
[30, 33, 35, 36, 39, 40, 42, 45, 48, 50, 51, 54, 55, 57, 60, 63, 65, 66, 69, 70, 72, 75, 78, 80, 81, 84, 85, 87, 90, 93, 95, 96, 99, 100]
[30, 33, 35, 36, 39, 40, 42, 45, 48, 50, 51, 54, 55, 57, 60, 63, 65, 66, 69, 70, 72, 75, 78, 80, 81, 84, 85, 87, 90, 93, 95, 96, 99, 100]
[33, 35, 36, 39, 40, 42, 45, 48, 50, 51, 54, 55, 57, 60, 63, 65, 66, 69, 70, 72, 75, 78, 80, 81, 84, 85, 87, 90, 93, 95, 96, 99]
[33, 35, 36, 39, 40, 42, 45, 48, 50, 51, 54, 55, 57, 60, 63, 65, 66, 69, 70, 72, 75, 78, 80, 81, 84, 85, 87, 90, 93, 95, 96, 99, 100]