簡體   English   中英

我們如何修復這個算法來找到最小的數字

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM