[英]How could we fix this algorithm to find the smallest number
我們想找到從給定長的數字創建的最小數字。
我們正在執行以下編程任務: https://www.codewars.com/kata/573992c724fc289553000e95
您有一個由數字組成的正數 n。 您最多可以做一個操作:選擇數字中的一個數字的索引,在該索引處刪除該數字,然后將其插入到另一個數字或數字的同一位置,以找到您可以獲得的最小數字。
#Task:根據語言返回一個數組或一個元組或一個字符串(參見“示例測試”)
1) the smallest number you got
2) the index i of the digit d you took, i as small as possible
3) the index j (as small as possible) where you insert this digit d to have the smallest number.
例子:
smallest(261235) --> [126235, 2, 0] or (126235, 2, 0) or "126235, 2, 0"
126235 是通過在索引 2 處取 1 並將其放在索引 0 處得到的最小數字
smallest(209917) --> [29917, 0, 1] or ...
[29917, 1, 0] could be a solution too but index `i` in [29917, 1, 0] is greater than
index `i` in [29917, 0, 1].
29917 是通過在索引 0 處取 2 並將其放在索引 1 中得到的最小數字,它給出了 029917,即數字 29917。
smallest(1000000) --> [1, 0, 6] or ...
-> 我們寫過:
public class ToSmallest {
public static long[] smallest /*🔢*/ (long n) {
System.out.println("\n\nn: "+n);
StringBuilder input = new StringBuilder(String.valueOf(n));
long min = Long.MAX_VALUE;
int minIndex = -1;
//We find the minimum and its index
for(int i=input.length()-1; n>0; i--){
long digit = n%10;
if(min!=Math.min(min,digit)){
minIndex='\0'+i;
}
min = Math.min(min, digit);
n /= 10;
}
System.out.println("min: "+min);
System.out.println("minIndex: "+minIndex);
//We put the minimum as first digit
input = input.deleteCharAt(minIndex);
System.out.println("input: "+input);
input = input.insert(0,min);
System.out.println("input: "+input);
return new long[]{Long.parseLong(input.toString()),minIndex,0};
}
}
我們認為這是不完整的,因為我們假設在所有情況下我們都可以通過以下方式創建最小值:
1) Find the min digit
2) Remove it from where it was
3) Insert it at start
但是,作為單元測試:
import static org.junit.Assert.*;
import java.util.Arrays;
import org.junit.Test;
public class ToSmallestTest {
private static void testing(long n, String res) {
assertEquals(res,
Arrays.toString(ToSmallest.smallest(n)));
}
@Test
public void test() {
System.out.println("Basic Tests smallest");
testing(261235, "[126235, 2, 0]");
testing(209917, "[29917, 0, 1]");
testing(285365, "[238565, 3, 1]");
testing(269045, "[26945, 3, 0]");
testing(296837, "[239687, 4, 1]");
}
}
-> 代碼在第二次測試中失敗。
我們如何改進算法?
編輯:閱讀 Norbert Dopjera 的回答后,我們嘗試過:
public class ToSmallest {
public static long[] smallest (long n) {
System.out.println("\n\nn: "+n);
StringBuilder input = new StringBuilder(String.valueOf(n));
long min = Long.MAX_VALUE;
int minIndex = -1;
int numberMinsFound = -1; //🔢
//We find the minimum and its index
while(numberMinsFound == minIndex){ //🔢
for(int i=input.length()-1; n>0; i--){
long digit = n%10;
if(min!=Math.min(min,digit)){
minIndex='\0'+i;
}
min = Math.min(min, digit);
n /= 10;
numberMinsFound++; //🔢
}
}
System.out.println("min: "+min);
System.out.println("minIndex: "+minIndex);
//We put the minimum as first digit
input = input.deleteCharAt(minIndex);
System.out.println("input: "+input);
input = input.insert(0,min);
System.out.println("input: "+input);
return new long[]{Long.parseLong(input.toString()),minIndex,0};
}
}
然而,第二次測試仍然是錯誤的:
n: 209917
min: 0
minIndex: 1
input: 29917
input: 029917
expected:<[29917, [0, 1]]> but was:<[29917, [1, 0]]>
第二個測試實際上失敗了,因為您正在使用字符串。 您的算法會發現最小數字 0 將其放在前面,因此它會創建以下字符串:“029917”,但由於您正在針對值為“29917”的字符串進行測試,因此測試將失敗。 您獲得的數字是您可以從提供的數字中獲得的最低數字,並且您可以進行操作。 因此,您的方法在這里是有效的。
編輯:您實際上可以通過以下方式改進您的代碼。 如果最小值僅在最低索引處找到,即最小值已經是第一個數字,那么您應該搜索第二個最小值。 如果第二個最小值再次在第二個 position 已經找到第三個最小值等等。直到你找到第 N 個最小值,它沒有放在它可能的最低索引處並將它放在那里。 這是第三次測試實際測試的內容。 您會發現數字 2 是最小數字,但由於它一開始已經是 position,您應該繼續找到第二個最小值,數字 3,並將其放置在您找到的上一個最小值之后並且不必移動。
真的,問題是:是什么使它成為邊緣案例? 我們需要解決這個特定示例的一般特征是什么?
例如,最初的反應可能是“好吧,我們把零放在前面,所以導致一個數字的位數更少……所以,解決方案是:檢查我們是否移動了一個零到前面”(是的,這是我最初的反應;))
Bzzzzt,錯了,那只會解決這個特殊情況。 不是一般的。
例如,考慮一個測試用例:439987,得到最小的數字 349987。但是:是解決方案“[349987, 1, 0]”(移動 3),或“[349987, 0, 1]”(移動4)?
根據挑戰的條款,它是最小的索引(“smallest i”) - 所以你想要生成的答案是“[349987, 0, 1]”
這也不必在前面,例如:考慮,124356 - 同樣,最小的數字是 123456 - 但那將是“[123456, 3, 4]”(移動 4),而不是“[123456, 4, 3]"(移動 3)
所以,忘記你的失敗案例為零的事實 - 這是無關緊要的。 重要的是一般規則:
如果您已經確定最小數字涉及交換相鄰數字,那么解決方案是“最小 i” - 即,它將(較大的)數字向后移動一個位置,而不是向前移動(較小的)數字。
編輯添加偽代碼
在我做之前,有幾點:考慮那個例子 124356,其中 3 和 4 交換位置。 “3”和“4”都不是數字中的最小數字(即數字“1”),所以你不能假設它總是會移動的最小數字。
這意味着你將不得不有一個循環。
一旦你談到循環,那么各種性能優化都是可能的——但我不認為這種考慮是挑戰的一個因素,所以我對此也不感興趣。
因此,考慮到這一點,偽代碼將是:
long minValue = MAX_LONG;
int fromIndex;
int toIndex;
for (int i=1; i < numDigits; i++) { // Move digit from position i
for (int j=0; j < i; j++) { // Bring forward to position j
if (digit[j] > digit[i]) { // So long as it's smaller
long value = <value of number after moving the digit>
if (value < minValue) {
minValue = value;
if (j == i-1) { // Adjacent digits? (as per last paragraph)
fromIndex = j; // the move from the smaller index
toIndex = i;
} else { // More general case, similar to what you wrote
fromIndex = i;
toIndex = j;
}
}
}
}
請記住 - 標准不是您確定移動的最小數字,而是您確定給出最小結果的最小 INDEX(“最小 i”)。
希望這可以幫助:)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.