我试图找出所有频率最高的数字。即如果最大频率为5,那么我需要在阵列中发生5次的所有数字。
让我们考虑以下数组示例:
1 8 7 8 9 2 1 9 6 4 3 5
在这里,最常见的数字是8,1,9,最高频率为2.我的预期输出是这样的:
int n=100;
int N=1000;
int data[] = new int[N];
Set<Integer> set = new HashSet<Integer>();
Random random = new Random();
for(int i=0;i<N;i++){
int number = random.nextInt(n);
data[i] = number;
set.add(number);
}
int frequency[] = new int[set.size()];
Integer[] distinct = set.toArray(new Integer[set.size()]);
for (int j=0;j<set.size();j++){
int count=0;
for(int k=0;k<N;k++){
if(distinct[j]==data[k]){
count = count+1;
}
}
frequency[j] = count;
}
在我的项目中,我试图找出最频繁的数字和最不频繁的数字。在这里,我想要最频繁的数字。
我生成了1000个类似于我的项目场景的随机数,并计算了不同的数字,然后计算了它们的出现次数。
int max = Integer.MIN_VALUE;
List<Integer> vals = new ArrayList<>();
for (int q=0; q < frequency.length; ++q) {
if (frequency[q] == max) {
vals.add(q);
}
else if (frequency[q] > max) {
vals.clear();
vals.add(q);
max = frequency[q];
}
}
for(int num : vals){
System.out.println(distinct[num]+" => "+frequency[num]);
}
在计算每个数字的频率后,我使用here的答案计算了大多数频率的数字,该答案是优化的。
func main() {
...
db, err := sql.Open("sqlite3", "./libreread.db")
CheckError(err)
defer db.Close()
...
}
这里,循环在第一个代码中使整个过程变慢。这只是大型代码和示例测试用例的一部分。
我想让这个过程更快,因为在实际情况下阵列中可能有大的元素。
有人有办法优化这些循环吗? 要么 获得结果的其他方法是什么?
感谢任何形式的帮助。
答案 0 :(得分:4)
我会为此使用流。它不会变得非常短,但是一旦你对溪流感到满意,它在概念上就会更简单。
Map<Integer, Long> frequencies = Arrays.stream(data)
.boxed()
.collect(Collectors.groupingBy(i -> i, Collectors.counting()));
if (frequencies.isEmpty()) {
System.out.println("No data");
} else {
long topFrequency = frequencies.values()
.stream()
.max(Long::compareTo)
.get();
int[] topNumbers = frequencies.entrySet()
.stream()
.filter(e -> e.getValue() == topFrequency)
.mapToInt(Map.Entry::getKey)
.toArray();
for (int number : topNumbers) {
System.out.println("" + number + " => " + topFrequency);
}
}
使用问题中的示例数据,它打印出所需的(仅在另一个不可预测的顺序中):
1 => 2
8 => 2
9 => 2
编辑:tucuxi问:为什么不用流来打印呢?当然,您可以使用更短更简单的代码:
frequencies.entrySet()
.stream()
.filter(e -> e.getValue() == topFrequency)
.mapToInt(Map.Entry::getKey)
.forEach(n -> System.out.println("" + n + " => " + topFrequency));
选择什么取决于要求和品味。我期待OP需要存储最高频率数字,所以我演示了如何做到这一点,并打印它们以显示结果。还有一些人认为流应该没有副作用,我会考虑打印到标准输出的副作用。但如果你愿意,可以使用它。
答案 1 :(得分:3)
此代码效率非常低,在最坏的情况下可能会在O(n^2)
中运行。
您可以通过构建Map<Integer,Integer>
来实现单个for循环的目标,其中密钥是您遇到的每个唯一编号,值是其频率。
获得Map
之后,找到具有最大频率的所有数字(只是迭代Map
的所有条目)是微不足道的。总运行时间为O(n)
。
int maxFreq = Integer.MIN_VALUE;
Map<Integer,Integer> freqs = new HashMap<>();
for(int i=0;i<N;i++){
int number = random.nextInt(n);
data[i] = number;
Integer freq = freqs.get(number);
if (freq != null) {
freq = freq + 1;
} else {
freq = 1;
}
freqs.put(number,freq);
if (freq > maxFreq)
maxFreq = freq;
}
for(Map.Entry<Integer,Integer> entry : freqs.entrySet()) {
if (entry.getValue().equals(maxFreq)) {
System.out.println(entry.getKey() +" => "+ maxFreq);
}
}
答案 2 :(得分:2)
+-----+----+-----+
| _1| _2|count|
+-----+----+-----+
|oded2|4818| 2|
| oded|4918| 2|
| oded|5018| 2|
+-----+----+-----+
答案 3 :(得分:1)
我会使用Java8流。在某些情况下,您甚至可以使用并行流来提高性能。我将如何做到这一点:
d = fetch(c,s) returns data for all fields from the Yahoo! website for the indicated security.
输出:
public static void main(String[] args) {
List<Integer> integers = Arrays.asList(1, 8, 7, 8, 9, 2, 1, 9, 6, 4, 3, 5);
//Here we have statistics of frequency for all numbers
LinkedHashMap<Integer, Integer> statistics = integers.stream().distinct()
.collect(Collectors.toMap(Function.identity(), number -> Collections.frequency(integers, number)))
.entrySet().stream().sorted(Collections.reverseOrder(Comparator.comparing(Map.Entry::getValue)))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (o1, o2) -> o1, LinkedHashMap::new));
//Calculate max frequency
Integer maxFrequency = statistics.entrySet().stream()
.max(Comparator.comparingInt(Map.Entry::getValue))
.map(Map.Entry::getValue).orElse(null);
//Collect max frequent numbers to a map
Map<Integer, Integer> topFrequentNumbers = statistics.entrySet().stream()
.filter(o -> o.getValue().equals(maxFrequency))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
//Print
topFrequentNumbers.forEach((number, frequency) -> System.out.println(number + " => " + frequency));
}
正如我所提到的,您可以使用并行流并提取一些部分来提高性能。
答案 4 :(得分:1)
我认为这是最简单的最优(O(n)
,对于这个问题)答案。与许多其他答案一样,它计算所有整数的频率。与其他人不同,它不需要第二次传递来找到最频繁的结果,特别是不执行按频率排序,如果您只需要“最频繁”,那就太过分了。此外,短代码更容易调试。
public static ArrayList<Integer> mostFrequent(int [] numbers) {
HashMap<Integer, Integer> frequencies = new HashMap<>();
ArrayList<Integer> mostFrequent = new ArrayList<>();
int greatestFrequency = 0;
for (int n : numbers) {
// build number -> frequency of number map
int f = frequencies.getOrDefault(n, 0) + 1;
frequencies.put(n, f);
if (f > greatestFrequency) {
// this number is more frequent than all others:
// it is now the sole, most frequent, number: no ties
mostFrequent.clear();
greatestFrequency = f;
}
if (f == greatestFrequency) {
// this number is as frequent as the most frequent:
// add it to the list of numbers tied for this privilege
mostFrequent.add(n);
}
}
// print out the final list of numbers that are tied for "most frequent"
for (int n : mostFrequent) {
System.out.println(n + " => " + greatestFrequency);
}
}
请注意,null
仅针对空列表返回。抛出异常也可能有效。只需要很少的工作,这段代码就可以接受任何Iterable<Number>
,但这会让它更难理解。我怀疑OP想要在生产系统中使用它。