Я написал реализацию Java, которая включает в себя несколько алгоритмов:
Код - реализация
CartesianPower.java :
import java.util.Arrays;
/**
* Cartesian power of n elements.
* <h2>Refer:</h2>
* <li><a href="https://stackoverflow.com/questions/52977819">Original question, with radix & iteration solution.</a>
* <li><a href="https://stackoverflow.com/questions/53012861">Additional question, with recursion solution.</a>
*
* <h2>Algorithms:</h2>
* <li><b>Radix</b>: intuitive, but not very efficient,
* <li><b>Iteration</b>: best choice, most efficient & scalable,
* <li><b>Recursion</b>: elegant, but not very intuitive, and performance is bad (or even stack overflow) for large input size,
* <li>
*
* @author eric
* @date Oct 25, 2018 4:53:34 PM
*/
public class CartesianPower {
public static final int MIN_ARR_LEN = 2; // min length of input array ,
public static final int MAX_ARR_LEN = 10; // max length of input array,
public static final int ARR_PRINT_LEN_LIMIT = 6; // max array length, that is allowed to print detailed output items to console,
/**
* Print cartesian power of array elements, by converting between radix.
* <p>
* time complexity: O(n) for each output item, and O(n^(n+1)) overall.
*
* @param arr
* an array with length between [Character.MIN_RADIX, Character.MAX_RADIX]
* @return count of cartesian, or -1 if radix is out of range,
*/
public static <T> long cartesianPowerViaRadix(T arr[]) {
int len = arr.length; // length of array, also the radix,
// check radix range,
if (len < MIN_ARR_LEN || len > MAX_ARR_LEN) {
System.out.printf("%d is out of range [%d, %d]\n", len, MIN_ARR_LEN, MAX_ARR_LEN);
return -1;
}
long count = 0;
long totalCount = (long) Math.pow(len, len);
T tmpArr[] = Arrays.copyOf(arr, len);
while (count < totalCount) {
String baseStr = Long.toString(count, len);
for (int i = 0; i < baseStr.length(); i++) {
char c = baseStr.charAt(i);
int r = Integer.parseInt(String.valueOf(c), len);
tmpArr[i] = arr[r];
}
for (int i = baseStr.length(); i < len; i++) {
tmpArr[i] = arr[0];
}
printArr(tmpArr);
count++;
}
System.out.printf("input arr: %s, total count: %d\n", Arrays.toString(arr), count);
return count;
}
/**
* Print cartesian power of array elements, by a single iteration.
* <p>
* time complexity:
* <li>When don't print to console
* <p>
* O(1) for each output item, and O(n^n) overall.
* <li>When print to console
* <p>
* O(n) for each output item, and O(n^(n+1)) overall.
*
* @param n
* array length, array is assumed as numbers start from 0 and increase by 1, e.g [0, 1, 2],
* <p>
* it should be within range [CartesianProduct.MIN_ARR_LEN, CartesianProduct.MAX_ARR_LEN]
* @return count of cartesian, or -1 if array length is out of range,
*/
public static long cartesianPowerViaIterationN(int n) {
// check range of array length,
if (n < MIN_ARR_LEN || n > MAX_ARR_LEN) {
System.out.printf("input length [%d] is out of range [%d, %d]\n", n, MIN_ARR_LEN, MAX_ARR_LEN);
return -1;
}
long startTime = System.currentTimeMillis();
long count = 0;
int tmpArr[] = new int[n];
Arrays.fill(tmpArr, 0);
// initial case,
printArr(tmpArr); // all elements as 0,
count++;
int i = n - 1;
while (i >= 0) {
tmpArr[i]++;
if (tmpArr[i] == n) {
tmpArr[i] = 0;
i--;
} else {
printArr(tmpArr);
i = n - 1;
count++;
}
}
long during = (System.currentTimeMillis() - startTime); // during in milliseconds,
System.out.printf("input arr length: %d, total count: %d, during: %d ms\n", n, count, during);
return count;
}
/**
* Print cartesian power of array elements, by a single iteration.
* <p>
* time complexity:
* <li>When don't print to console
* <p>
* O(1) for each output item, and O(n^n) overall.
* <li>When print to console
* <p>
* O(n) for each output item, and O(n^(n+1)) overall.
*
* @param arr
* input array, could be of any type without order requirement,
* <p>
* its length should be within range [CartesianProduct.MIN_ARR_LEN, CartesianProduct.MAX_ARR_LEN]
* @return count of cartesian, or -1 if array length is out of range,
* @return
*/
public static <T> long cartesianPowerViaIteration(T arr[]) {
int n = arr.length;
// check range of array length,
if (n < MIN_ARR_LEN || n > MAX_ARR_LEN) {
System.out.printf("input length [%d] is out of range [%d, %d]\n", n, MIN_ARR_LEN, MAX_ARR_LEN);
return -1;
}
long startTime = System.currentTimeMillis();
long count = 0;
T tmpArr[] = Arrays.copyOf(arr, n); // tmp array,
int idxArr[] = new int[n]; // index mapping array,
Arrays.fill(idxArr, 0); // all indices as 0,
// initial case,
printIndirectionArr(idxArr, arr, tmpArr);
count++;
int i = n - 1;
while (i >= 0) {
idxArr[i]++;
if (idxArr[i] == n) {
idxArr[i] = 0;
i--;
} else {
printIndirectionArr(idxArr, arr, tmpArr);
i = n - 1;
count++;
}
}
long during = (System.currentTimeMillis() - startTime); // during in milliseconds,
System.out.printf("input arr length: %d, total count: %d, during: %d ms\n", n, count, during);
return count;
}
/**
* Print cartesian power of array elements, via recursion, with raw int[] input.
* <p>
* Time complexity: O(1) for each output item, and O(n^n) overall.
*
* @param arr
* @return count of cartesian, or -1 if radix is out of range,
*/
public static <T> long cartesianPowerViaRecursion(int arr[]) {
int n = arr.length;
// check range of array length,
if (n < MIN_ARR_LEN || n > MAX_ARR_LEN) {
System.out.printf("input length [%d] is out of range [%d, %d]\n", n, MIN_ARR_LEN, MAX_ARR_LEN);
return -1;
}
long startTime = System.currentTimeMillis();
int tmpArr[] = Arrays.copyOf(arr, n);
long count = cartesianPowerViaRecursion(arr, tmpArr, 0);
long during = (System.currentTimeMillis() - startTime); // during in milliseconds,
System.out.printf("input arr length: %d, total count: %d, during: %d ms\n", n, count, during);
return count;
}
/**
* Print cartesian power of array elements, via recursion, the inner function, with raw int[] input.
*
* @param arr
* input array,
* @param tmpArr
* tmp array,
* @param t
* current index in tmp array,
* @return count of cartesian,
*/
private static <T> long cartesianPowerViaRecursion(int arr[], int tmpArr[], int t) {
int n = arr.length;
long count = 0;
if (t == n) {
printArr(tmpArr);
count++;
} else {
for (int i = 0; i < n; i++) {
tmpArr[t] = arr[i];
count += cartesianPowerViaRecursion(arr, tmpArr, t + 1);
}
}
return count;
}
/**
* Print cartesian power of array elements, via recursion.
* <p>
* Time complexity: O(1) for each output item, and O(n^n) overall.
*
* @param arr
* @return count of cartesian, or -1 if radix is out of range,
*/
public static <T> long cartesianPowerViaRecursion(T arr[]) {
int n = arr.length;
// check range of array length,
if (n < MIN_ARR_LEN || n > MAX_ARR_LEN) {
System.out.printf("input length [%d] is out of range [%d, %d]\n", n, MIN_ARR_LEN, MAX_ARR_LEN);
return -1;
}
long startTime = System.currentTimeMillis();
T tmpArr[] = Arrays.copyOf(arr, n);
long count = cartesianPowerViaRecursion(arr, tmpArr, 0);
long during = (System.currentTimeMillis() - startTime); // during in milliseconds,
System.out.printf("input arr length: %d, total count: %d, during: %d ms\n", n, count, during);
return count;
}
/**
* Print cartesian power of array elements, via recursion, the inner function.
*
* @param arr
* input array,
* @param tmpArr
* tmp array,
* @param t
* current index in tmp array,
* @return count of cartesian,
*/
private static <T> long cartesianPowerViaRecursion(T arr[], T tmpArr[], int t) {
int n = arr.length;
long count = 0;
if (t == n) {
printArr(tmpArr);
count++;
} else {
for (int i = 0; i < n; i++) {
tmpArr[t] = arr[i];
count += cartesianPowerViaRecursion(arr, tmpArr, t + 1);
}
}
return count;
}
/**
* Print int array, only when length <= limit.
*
* @param arr
* @return boolean, indicate whether printed,
*/
private static boolean printArr(int arr[]) {
if (arr.length > ARR_PRINT_LEN_LIMIT) {
return false;
}
System.out.println(Arrays.toString(arr));
return true;
}
/**
* Print array, only when length <= limit.
*
* @param arr
* @return boolean, indicate whether printed,
*/
private static <T> boolean printArr(T[] arr) {
if (arr.length > ARR_PRINT_LEN_LIMIT) {
return false;
}
System.out.println(Arrays.toString(arr));
return true;
}
/**
* Print indirection array, only when length <= limit.
*
* @param idxArr
* contain index mapping,
* @param arr
* @param tmpArr
* @return boolean, indicate whether printed,
*/
private static <T> boolean printIndirectionArr(int idxArr[], T[] arr, T[] tmpArr) {
if (arr.length > ARR_PRINT_LEN_LIMIT) {
return false;
}
fillArrFromIndex(idxArr, arr, tmpArr); // fill tmp array,
System.out.println(Arrays.toString(tmpArr));
return true;
}
/**
* Fill tmp array with elements from array, according to index mapping.
*
* @param idxArr
* contain index mapping,
* @param arr
* @param tmpArr
*/
private static <T> void fillArrFromIndex(int idxArr[], T[] arr, T[] tmpArr) {
for (int i = 0; i < idxArr.length; i++) {
tmpArr[i] = arr[idxArr[i]];
}
}
}
Советы:
- Диапазондлина массива равна
[2, 10]
, чтобы избежать слишком долгого запуска. - Только если длина массива <= 6, будет выводиться подробный вывод, в противном случае выводится только сводка. </li>
Код - контрольный пример
(все контрольные примеры используют TestNG )
CartesianPowerRadixTest.java:
(контрольный пример - для алгоритма радиуса)
import org.testng.Assert;
import org.testng.annotations.Test;
/**
* CartesianPower radix - test.
*
* @author eric
* @date Oct 25, 2018 5:04:54 PM
*/
public class CartesianPowerRadixTest {
@Test
public void testOrderedNum() {
Integer arr[] = new Integer[] { 0, 1, 2 }; // len = 3,
Assert.assertEquals(CartesianPower.cartesianPowerViaRadix(arr), 27);
}
@Test
public void testRandomNum() {
Integer arr2[] = new Integer[] { 0, 2, 1 }; // len = 3,
Assert.assertEquals(CartesianPower.cartesianPowerViaRadix(arr2), 27);
}
@Test
public void testChar() {
Character charArr[] = new Character[] { 'a', 'b', 'c' }; // len = 3,
Assert.assertEquals(CartesianPower.cartesianPowerViaRadix(charArr), 27);
}
@Test
public void testObject() {
Object objectArr[] = new Object[] { 0, 'g', true }; // len = 3,
Assert.assertEquals(CartesianPower.cartesianPowerViaRadix(objectArr), 27);
}
@Test
public void testOutOfRange() {
Object tooShortArr[] = new Object[] { 1 }; // len = 1,
Assert.assertEquals(CartesianPower.cartesianPowerViaRadix(tooShortArr), -1);
Object tooLongArr[] = new Object[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
31, 32, 33, 34, 35, 36, 37, 38, 39 }; // len = 40,
Assert.assertEquals(CartesianPower.cartesianPowerViaRadix(tooLongArr), -1);
}
}
CartesianPowerIterationNTest.java:
(контрольный пример - для итерационного алгоритма - вводдлина п, обрay принимается как начало массива int с 0, увеличивается на 1)
import org.testng.Assert;
import org.testng.annotations.Test;
/**
* CartesianPower iteration N - test.
*
* @author eric
* @date Oct 26, 2018 1:44:01 PM
*/
public class CartesianPowerIterationNTest {
@Test
public void testSmall() {
int len = 3;
Assert.assertEquals(CartesianPower.cartesianPowerViaIterationN(len), 27);
}
@Test
public void testLarge() {
int len = 9;
Assert.assertEquals(CartesianPower.cartesianPowerViaIterationN(len), 387420489);
}
@Test
public void testOutOfRange() {
int tooShortLen = CartesianPower.MIN_ARR_LEN - 1;
Assert.assertEquals(CartesianPower.cartesianPowerViaIterationN(tooShortLen), -1);
int tooLongLen = CartesianPower.MAX_ARR_LEN + 1;
Assert.assertEquals(CartesianPower.cartesianPowerViaIterationN(tooLongLen), -1);
}
}
CartesianPowerIterationArrTest.java:
(тестовый пример - для алгоритма итерации - вводмассив любого типа без требования заказа)
import org.testng.Assert;
import org.testng.annotations.Test;
/**
* CartesianPower iteration array - test.
*
* @author eric
* @date Oct 26, 2018 2:48:50 PM
*/
public class CartesianPowerIterationArrTest {
@Test
public void testOrderedNum() {
Integer arr[] = new Integer[] { 0, 1, 2 }; // len = 3,
Assert.assertEquals(CartesianPower.cartesianPowerViaIteration(arr), 27);
}
@Test
public void testRandomNum() {
Integer arr2[] = new Integer[] { 0, 2, 1 }; // len = 3,
Assert.assertEquals(CartesianPower.cartesianPowerViaIteration(arr2), 27);
}
@Test
public void testChar() {
Character charArr[] = new Character[] { 'a', 'b', 'c' }; // len = 3,
Assert.assertEquals(CartesianPower.cartesianPowerViaIteration(charArr), 27);
}
@Test
public void testObject() {
Object objectArr[] = new Object[] { 0, 'g', true }; // len = 3,
Assert.assertEquals(CartesianPower.cartesianPowerViaIteration(objectArr), 27);
}
@Test
public void testLarge() {
Object arr[] = new Object[] { 1, 0, 2, 'c', 'a', 'b', true, false, null }; // len = 9,
Assert.assertEquals(CartesianPower.cartesianPowerViaIteration(arr), 387420489);
// Object arr2[] = new Object[] { 1, 0, 2, 'c', 'a', 'b', true, false, null, new Object() }; // len = 10,
// Assert.assertEquals(CartesianProduct.cartesianViaLoopArr(arr2), 10000000000L);
}
@Test
public void testOutOfRange() {
Object tooShortArr[] = new Object[] { 1 }; // len = 1,
Assert.assertEquals(CartesianPower.cartesianPowerViaIteration(tooShortArr), -1);
Object tooLongArr[] = new Object[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
31, 32, 33, 34, 35, 36, 37, 38, 39 }; // len = 40,
Assert.assertEquals(CartesianPower.cartesianPowerViaIteration(tooLongArr), -1);
}
}
CartesianPowerRecursiveRawTest.java:
(тестовый случай - для алгоритма рекурсии - входной массив имеет типint [])
import org.testng.Assert;
import org.testng.annotations.Test;
/**
* CartesianPower recursion array - raw int[] input - test.
*
* @author eric
* @date Oct 27, 2018 4:09:18 AM
*/
public class CartesianPowerRecursiveRawTest {
@Test
public void testOrderedNum() {
int arr[] = new int[] { 0, 1, 2 }; // len = 3
Assert.assertEquals(CartesianPower.cartesianPowerViaRecursion(arr), 27);
}
@Test
public void testRandomNum() {
int arr2[] = new int[] { 0, 2, 1 }; // len = 3,
Assert.assertEquals(CartesianPower.cartesianPowerViaRecursion(arr2), 27);
}
@Test
public void testLarge() {
int arr[] = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; // len = 9,
Assert.assertEquals(CartesianPower.cartesianPowerViaRecursion(arr), 387420489);
// int arr2[] = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; // len = 10,
// Assert.assertEquals(CartesianPower.cartesianViaRecursion(arr2), 10000000000L);
}
@Test
public void testOutOfRange() {
int tooShortArr[] = new int[] { 1 }; // len = 1,
Assert.assertEquals(CartesianPower.cartesianPowerViaRecursion(tooShortArr), -1);
int tooLongArr[] = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
33, 34, 35, 36, 37, 38, 39 }; // len = 40,
Assert.assertEquals(CartesianPower.cartesianPowerViaRecursion(tooLongArr), -1);
}
}
CartesianPowerRecursiveTest.java:
(тестовый случай - для алгоритма рекурсии - входной массив имеет универсальный тип T [])
import org.testng.Assert;
import org.testng.annotations.Test;
/**
* CartesianPower recursion array - test.
*
* @author eric
* @date Oct 26, 2018 11:45:27 PM
*/
public class CartesianPowerRecursiveTest {
@Test
public void testOrderedNum() {
Integer arr[] = new Integer[] { 0, 1, 2 };
Assert.assertEquals(CartesianPower.cartesianPowerViaRecursion(arr), 27);
}
@Test
public void testRandomNum() {
Integer arr2[] = new Integer[] { 0, 2, 1 }; // len = 3,
Assert.assertEquals(CartesianPower.cartesianPowerViaRecursion(arr2), 27);
}
@Test
public void testChar() {
Character charArr[] = new Character[] { 'a', 'b', 'c' }; // len = 3,
Assert.assertEquals(CartesianPower.cartesianPowerViaRecursion(charArr), 27);
}
@Test
public void testObject() {
Object objectArr[] = new Object[] { 0, 'g', true }; // len = 3,
Assert.assertEquals(CartesianPower.cartesianPowerViaRecursion(objectArr), 27);
}
@Test
public void testLarge() {
Object arr[] = new Object[] { 1, 0, 2, 'c', 'a', 'b', true, false, null }; // len = 9,
Assert.assertEquals(CartesianPower.cartesianPowerViaRecursion(arr), 387420489);
// Object arr2[] = new Object[] { 1, 0, 2, 'c', 'a', 'b', true, false, null, new Object() }; // len = 10,
// Assert.assertEquals(CartesianProduct.cartesianViaRecursion(arr2), 10000000000L);
}
@Test
public void testOutOfRange() {
Object tooShortArr[] = new Object[] { 1 }; // len = 1,
Assert.assertEquals(CartesianPower.cartesianPowerViaRecursion(tooShortArr), -1);
Object tooLongArr[] = new Object[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
31, 32, 33, 34, 35, 36, 37, 38, 39 }; // len = 40,
Assert.assertEquals(CartesianPower.cartesianPowerViaRecursion(tooLongArr), -1);
}
}