英文:
How could we fix this algorithm to find the smallest number
问题
Here is the translated code snippet:
public class ToSmallest {
public static long[] smallest(long n) {
StringBuilder input = new StringBuilder(String.valueOf(n));
long min = Long.MAX_VALUE;
int minIndex = -1;
// 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 = i;
}
min = Math.min(min, digit);
n /= 10;
}
// Put the minimum as the first digit
input = input.deleteCharAt(minIndex);
input = input.insert(0, min);
return new long[]{Long.parseLong(input.toString()), minIndex, 0};
}
}
Please note that the code you provided in your question appears to have a couple of issues that need to be addressed for it to work correctly. However, I've translated the code as it is without making any changes to its logic. If you have specific questions about improving the algorithm or resolving issues, please let me know, and I can provide further assistance.
英文:
We would like to find the smallest number created from the digits of a given long.
We are doing the following programming task: https://www.codewars.com/kata/573992c724fc289553000e95
You have a positive number n consisting of digits. You can do at most one operation: Choosing the index of a digit in the number, remove this digit at that index and insert it back to another or at the same place in the number in order to find the smallest number you can get.
#Task: Return an array or a tuple or a string depending on the language (see "Sample Tests") with
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.
Example:
smallest(261235) --> [126235, 2, 0] or (126235, 2, 0) or "126235, 2, 0"
126235 is the smallest number gotten by taking 1 at index 2 and putting it at index 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 is the smallest number gotten by taking 2 at index 0 and putting it at index 1 which gave 029917 which is the number 29917.
smallest(1000000) --> [1, 0, 6] or ...
-> We have written:
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='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};
}
}
'+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};
}
}
We think that is is incomplete because of we are assuming that in all cases we could create the minimum by:
1) Find the min digit
2) Remove it from where it was
3) Insert it at start
However, being the unit tests:
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]");
}
}
-> The code is failing at the second test.
How could we improve the algorithm?
EDIT: After reading Norbert Dopjera's answer we have tried:
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='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};
}
}
'+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};
}
}
However it stills being wrong with the second test:
n: 209917
min: 0
minIndex: 1
input: 29917
input: 029917
expected:<[29917, [0, 1]]> but was:<[29917, [1, 0]]>
答案1
得分: 2
第二个测试实际上失败是因为你正在处理字符串。你的算法会找到最小的数字 0 并将其放在最前面,因此会创建以下字符串:"029917",但由于你正在针对一个值为 "29917" 的字符串进行测试,测试将失败。你获得的数字是你可以从所提供的数字中使用允许的操作得到的最小数字。因此,你的方法在这里是有效的。
编辑:你实际上可以通过以下方式改进你的代码。如果最小值仅在最低索引处找到,即最小值已经是第一个数字,那么你应该搜索第二最小值。如果第二最小值再次在第二个位置,那么就找第三最小值,依此类推,直到找到第 N 个最小值,它不位于其最低可能索引处,并将其放在那里。这实际上是第三个测试要测试的内容。你会找到数字 2 作为最小数字,但由于它已经在第一个位置,你应该继续寻找第二最小值,即数字 3,并将它放在你之前找到的前一个最小值的右边,无需移动它。
英文:
Second test is actually failing because you are working with string's. You'r algorithm will find minimum number 0 put it in front and therefore it create's following string: "029917" but since you are testing against a string with value "29917" test will fail. Your obtained number is lowest number you can get from provided digit's with the operation's you are allowed to do. Your approach is therefore here valid.
Edit: you can actualy improve your code with following. If minimum is only found at lowest index, i.e minimum is already first number then you should search for second minimum. If second minimum is again at second position already find 3rd minimum etc.. until you find N-th minimum which is not placed at it's lowest possible index and place it there. This is what 3rd test is actually testing. You will find number 2 as minimum number but since it's already at first position you should continue to find second minimum, number 3, and place it right after previous minimum you found and didn't have to move.
答案2
得分: 1
以下是翻译好的内容:
实际上,问题是:是什么使得这成为一个边缘情况?我们需要解决这个特定示例的哪些一般性特征?
例如,最初的反应可能是:“嗯,我们把零放在前面,所以得到一个位数较小的数字......因此,解决方案是:检查我们是否在将零移到前面”(是的,这是我的最初反应😉)
嗡嗡声,错误!这只会解决这个特定情况,而不是一般情况。
例如,考虑一个测试案例:439987,得到最小的数字349987。
但问题是:“[349987,1,0]”(将3移动),还是“[349987,0,1]”(将4移动)?
根据挑战的条件,是最小的索引(“最小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++) { // 从位置i移动数字
for (int j=0; j < i; j++) { // 向前移到位置j
if (digit[j] > digit[i]) { // 只要它更小
long value = <移动数字后的数值>
if (value < minValue) {
minValue = value;
if (j == i-1) { // 相邻的数字?(根据上一段)
fromIndex = j; // 从较小的索引移动
toIndex = i;
} else { // 更一般的情况,类似于你写的
fromIndex = i;
toIndex = j;
}
}
}
}
记住-标准不是你要识别哪个数字移动最小,而是你要确定给出最小结果的最小索引(“最小i”)。
希望对你有所帮助
英文:
Really, the question is : What makes that an edge case ? What are the general features of this specific example that we need to address ?
For example, an initial reaction might be "well, we're putting the zero up front, so resulting in a number with a smaller number of digits ...... so, solution is : check if we're moving a zero to the front" (and yes, that was my initial reaction )
Bzzzzt, wrong ! That would only be solving THIS particular case, not the general.
For example, consider a test case : 439987, resulting in the smallest number 349987.
But : was is solution "[349987, 1, 0]" (moving the 3), or "[349987, 0, 1]" (moving the 4) ?
By the terms of the challenge, it's the smallest index ("smallest i") - so the answer you want to generate is "[349987, 0, 1]"
This also doesn't have to be at the front ! For example, consider : 124356 - again, smallest number is 123456 - but that would be "[123456, 3, 4]" (moving the 4), not "[123456, 4, 3]" (moving the 3)
So, forget the fact that your failing case has a zero - that's irrelevant. What's important is the general rule :
If you have decided that the smallest number involves swapping around adjacent digits, then the solution is "smallest i" - ie, it is moving the (larger) digit back one spot, rather than moving the (smaller) digit forward.
EDIT TO ADD PSEUDOCODE
Before I do though, couple of points :
Consider that example 124356, where the 3 and 4 swap places. Neither '3' nor '4' are the smallest digit in the number (which is the digit '1'), so you can't assume that it is always going to be the smallest digit that's going to move.
What this means is you are going to have to have a loop.
As soon as you talk about a loop, then all sorts of performance optimisations are possible - but I don't see that consideration as a factor in the challenge, so I'm not interested in that either.
So, with that in mind, pseudocode will be :
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;
}
}
}
}
Remember - the criteria is NOT that you identify the smallest digit that moves, but that you identify the smallest INDEX ("smallest i") that gives the smallest result.
Hope this helps
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论