Java 将双精度列表转换为分组字符串
程序有一个输入和一个双精度列表,输出需要是一个字符串,包含按其值分组的列表值。如果列表值相等,则将对它们进行分组。 比如: 输入9,77,5,5,31=>输出9,77 2*5 31 我在Java中用C创建了一个算法,我认为这几乎与此相同,但我不确定它的速度或代码质量是否可以改进,或者它是否有一些我看不到的错误。下面是具有更多输入、输出示例的算法Java 将双精度列表转换为分组字符串,java,c#,algorithm,performance,testing,Java,C#,Algorithm,Performance,Testing,程序有一个输入和一个双精度列表,输出需要是一个字符串,包含按其值分组的列表值。如果列表值相等,则将对它们进行分组。 比如: 输入9,77,5,5,31=>输出9,77 2*5 31 我在Java中用C创建了一个算法,我认为这几乎与此相同,但我不确定它的速度或代码质量是否可以改进,或者它是否有一些我看不到的错误。下面是具有更多输入、输出示例的算法 List<double> input = new List<double> { 11, 32, 32, 43
List<double> input = new List<double> { 11, 32, 32, 43}; // output 11 2*32 43
//List<double> input = new List<double> { 11, 11, 43, 43 }; // output 2*11 2*43
//List<double> input = new List<double> { 10, 11, 12, 13, 14, 15, 16 }; // output 10 11 12 13 14 15 16
//List<double> input = new List<double> { 11, 11, 11, 11, 11 }; // output 5 * 11
//List<double> input = new List<double> { 11, 11, 32, 22, 22, 22, 4, 10, 10 }; // output 2*11 32 3*22 4 2*10
string listAsString = string.Empty;
double nextElem = double.MinValue;
for (int i = 0; i < input.Count; i++)
{
double currentElem = input[i];
if (i + 1 < input.Count)
{
nextElem = input[i + 1];
}
int equalCount = 0;
while (currentElem.Equals(nextElem) && i < input.Count)
{
equalCount++;
i++;
currentElem = nextElem;
if (i < input.Count)
{
nextElem = input[i];
}
}
if (equalCount < 2)
{
listAsString += currentElem + " ";
}
else
{
listAsString += equalCount + "*" + currentElem + " ";
i--;
}
}
Console.WriteLine(listAsString);
请让我知道,如果你注意到一些错误或看到一些可以做的改进
此外,如果您知道此要求的另一个实现,请添加它,以便可以对算法之间的结果、速度和代码质量进行比较。。。并找到处理此问题的最佳方法。就我个人而言,我在这里看到了字典使用的巨大潜力,下面是我使用字典实现的一个快速解决方案:
var input = new List<double> { 9, 77, 5, 5, 31 };
var dict = new Dictionary<double, int>();
var listAsString = new StringBuilder();
foreach (var item in input)
{
if (dict.ContainsKey(item))
dict[item]++;
else
dict[item] = 1;
}
foreach (var item in dict)
{
listAsString.Append(item.Value > 1 ? $"{item.Value}*{item.Key} " : $"{item.Key} ");
}
Console.WriteLine(listAsString);
但是,我相信你的方法写得很好,虽然可读性比字典的要差一点,但是你的解决方案的主要缺陷是,你在构建最终字符串时使用了字符串,你肯定应该使用StringBuilder,我在您的方法中介绍了StringBuilder,并对这三种方法进行了比较:
Dictionary | Your method | GroupBy method
------------------------------------------------
2 ms | 0 ms | 5 ms n=3
0 ms | 0 ms | 0 ms n=6
0 ms | 0 ms | 0 ms n=12
0 ms | 0 ms | 0 ms n=24
0 ms | 0 ms | 0 ms n=48
0 ms | 0 ms | 0 ms n=96
0 ms | 0 ms | 0 ms n=192
0 ms | 0 ms | 0 ms n=384
0 ms | 0 ms | 0 ms n=768
0 ms | 0 ms | 0 ms n=1536
1 ms | 0 ms | 1 ms n=3072
3 ms | 2 ms | 3 ms n=6144
5 ms | 4 ms | 6 ms n=12288
8 ms | 7 ms | 14 ms n=24576
14 ms | 13 ms | 25 ms n=49152
31 ms | 32 ms | 66 ms n=98304
80 ms | 59 ms | 146 ms n=196608
149 ms | 123 ms | 294 ms n=393216
246 ms | 218 ms | 504 ms n=786432
483 ms | 428 ms | 1040 ms n=1572864
999 ms | 873 ms | 2070 ms n=3145728
1995 ms | 1784 ms | 3950 ms n=6291456
您的解决方案总是最快的,如果您想加快速度,请保留您的解决方案,但将其更改为使用StringBuilder,使用listAsString.AppendcurrentElem+而不是listAsString+=currentElem+
如果您只处理n<1000的集合,则可以使用GroupBy。如果您希望解决可读性问题而不是速度问题,则可以使用字典解决方案。就个人而言,我在这里看到字典使用的巨大潜力,下面是我用字典实现的一个快速解决方案:
var input = new List<double> { 9, 77, 5, 5, 31 };
var dict = new Dictionary<double, int>();
var listAsString = new StringBuilder();
foreach (var item in input)
{
if (dict.ContainsKey(item))
dict[item]++;
else
dict[item] = 1;
}
foreach (var item in dict)
{
listAsString.Append(item.Value > 1 ? $"{item.Value}*{item.Key} " : $"{item.Key} ");
}
Console.WriteLine(listAsString);
但是,我相信你的方法写得很好,虽然可读性比字典的要差一点,但是你的解决方案的主要缺陷是,你在构建最终字符串时使用了字符串,你肯定应该使用StringBuilder,我在您的方法中介绍了StringBuilder,并对这三种方法进行了比较:
Dictionary | Your method | GroupBy method
------------------------------------------------
2 ms | 0 ms | 5 ms n=3
0 ms | 0 ms | 0 ms n=6
0 ms | 0 ms | 0 ms n=12
0 ms | 0 ms | 0 ms n=24
0 ms | 0 ms | 0 ms n=48
0 ms | 0 ms | 0 ms n=96
0 ms | 0 ms | 0 ms n=192
0 ms | 0 ms | 0 ms n=384
0 ms | 0 ms | 0 ms n=768
0 ms | 0 ms | 0 ms n=1536
1 ms | 0 ms | 1 ms n=3072
3 ms | 2 ms | 3 ms n=6144
5 ms | 4 ms | 6 ms n=12288
8 ms | 7 ms | 14 ms n=24576
14 ms | 13 ms | 25 ms n=49152
31 ms | 32 ms | 66 ms n=98304
80 ms | 59 ms | 146 ms n=196608
149 ms | 123 ms | 294 ms n=393216
246 ms | 218 ms | 504 ms n=786432
483 ms | 428 ms | 1040 ms n=1572864
999 ms | 873 ms | 2070 ms n=3145728
1995 ms | 1784 ms | 3950 ms n=6291456
您的解决方案总是最快的,如果您想加快速度,请保留您的解决方案,但将其更改为使用StringBuilder,使用listAsString.AppendcurrentElem+而不是listAsString+=currentElem+
如果您只对n<1000的集合进行操作,则可以使用GroupBy。如果您希望解决可读性问题而不是速度问题,请使用字典解决方案。因为要求仅对连续的相等值进行分组,另一个答案中提到的Dictionary和LINQ GroupBy方法不适用,因为它们将为输入序列(如1,2,1)生成不正确的结果。此外,除了最终聚合方法外,并没有标准的LINQ方法来进行这种分组,但它只不过是for/foreach循环等效的低效方法 简而言之,您的算法是此类任务的最佳算法。但实施并非如此 主要的瓶颈是所提到的字符串连接,在另一个答案中也提到了这一点,通过使用StringBuilder类可以很容易地解决。一旦你这样做了,性能就会很好 我在实现中看到的另一个问题是使用特殊值double.MinValue、重复的角点大小写检查、在主体内部递减for循环变量等。因此,尽管它可能有效,而且我没有直接看到错误,但仅仅读取实现就很难遵循算法逻辑并发现潜在的错误。算法本身非常简单,我会这样实现:
static string ListAsString(List<double> input)
{
var sb = new StringBuilder();
for (int i = 0; i < input.Count; )
{
var value = input[i];
int count = 1;
while (++i < input.Count && input[i] == value)
count++;
if (sb.Length > 0) sb.Append(' ');
if (count > 1) sb.Append(count).Append('*');
sb.Append(value);
}
return sb.ToString();
}
这是国际海事组织比较容易遵循的。请注意,没有重复代码,没有特殊值,循环变量i仅在外部循环体中的一个位置执行。同样,这与StringBuilder的使用所提供的性能无关,只不过是可读性、冗余消除和不易出错。因为要求只对连续的相等值进行分组,另一个答案中提到的Dictionary和LINQ GroupBy方法不适用,因为它们将为输入序列(如1,2,1)生成不正确的结果。此外,除了最终聚合方法外,并没有标准的LINQ方法来进行这种分组,但它只不过是for/foreach循环等效的低效方法 简而言之,您的算法是此类任务的最佳算法。但实施并非如此 主要的瓶颈是所提到的字符串连接,在另一个答案中也提到了这一点,通过使用StringBuilder类可以很容易地解决。一旦你这样做了,性能就会很好 我在实现中看到的另一个问题是使用特殊值double.MinValue、重复的角大小写检查、在主体内部递减for循环变量等 bably可以工作,我没有直接看到一个bug,仅仅阅读实现就很难遵循算法逻辑并发现潜在的bug。算法本身非常简单,我会这样实现:
static string ListAsString(List<double> input)
{
var sb = new StringBuilder();
for (int i = 0; i < input.Count; )
{
var value = input[i];
int count = 1;
while (++i < input.Count && input[i] == value)
count++;
if (sb.Length > 0) sb.Append(' ');
if (count > 1) sb.Append(count).Append('*');
sb.Append(value);
}
return sb.ToString();
}
这是国际海事组织比较容易遵循的。请注意,没有重复代码,没有特殊值,循环变量i仅在外部循环体中的一个位置执行。同样,这与StringBuilder使用所提供的性能无关,只是可读性、冗余消除和不易出错。对于1,3,1,输出是1 3 1或2*1 3?这更像是代码评审的问题。嗨,安东宁,对于输入1 3 1,输出将是1 3 1。只有连续相等的数字将被分组。嗨,大卫,谢谢你的建议,我也将在那里发布!对于1,3,1,输出是1 3 1或2*1 3?这似乎更像是codereview的问题。se?Hi Antonín,对于输入1 3 1,输出将是1 3 1。只有连续相等的数字将被分组。嗨,大卫,谢谢你的建议,我也将在那里发布!你的努力肯定对我有用。在你最终将linq添加到你的答案之前,他一直在玩弄linq。NetFiddle运行了超过1mil的内存。当我接近1600万个元素时,我用字典方法遇到了内存问题。。很高兴它是有用的:-嗨Peroxy,谢谢你的详细解释!你的答案似乎涵盖了所有可能的nice实现。这很好,但OP指出只有连续的相等数字才会被分组。你的两个解给出了不同的结果。@AntonínLejsek是对的,我的解是不正确的,如果连续相等的数字只应该分组,我一开始没有看到这一点。。你应该参考伊万·斯托夫的答案。你的努力肯定对我有用。在你最终将linq添加到你的答案之前,他一直在玩弄linq。NetFiddle运行了超过1mil的内存。当我接近1600万个元素时,我用字典方法遇到了内存问题。。很高兴它是有用的:-嗨Peroxy,谢谢你的详细解释!你的答案似乎涵盖了所有可能的nice实现。这很好,但OP指出只有连续的相等数字才会被分组。你的两个解给出了不同的结果。@AntonínLejsek是对的,我的解是不正确的,如果连续相等的数字只应该分组,我一开始没有看到这一点。。你应该参考伊万·斯托夫的答案。