Java程序使用列表

时间:2016-02-27 18:33:53

标签: java

我必须编写一个程序,找到从0到任意数字n的所有幸运数字 这是一个幸运数字:

考虑自然数的序列 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 ...... ....................................。
删除每隔一个数字产生序列
1,3,5,7,9,11,13,15,17,19,21,23 ..............................。
删除每三个数字产生序列
1,3,7,9,13,15,19,21,25 ..............................。

此过程通过删除第四个,第五个......等等无限期地继续,直到经过一定数量的步骤后,某些自然数无限期地保留。这些被称为幸运数字。

我决定尝试使用ArrayList。但我似乎无法想象这一点。我现在已经好几天了 这是代码:

import java.util.*;
class luckyy
{
public static void main(String args[])
{

    Scanner scan = new Scanner(System.in);
    System.out.println("Enter n: ");

    int i, n, index;
    n = scan.nextInt();     
    ArrayList <Integer> ele = new ArrayList <Integer> (n);
    //storing in a list
    for(i = 1;i<=n;i++)
    {
        ele.add(i);
    }
    System.out.println(ele);
    int count = 2;
    index = 1;
    System.out.println("Size: "+ele.size());
    while(count<ele.size())
    {
        for(i = 0;i<ele.size();i++)
        {
            int chk = ele.get(i);
            if(chk%count == 0)
            {
                ele.remove(i);
            }                
        }
        count++;           
    }
    System.out.print(ele);        
}
}

这给出了输出:
[1, 5, 7]
当所需的输出是:
[1, 3, 7]

所以,对不起,如果这段代码太糟糕了以至于令人反感哈哈...... 但我真的很感激任何帮助。我刚刚开始作为一名程序员,并且有很多东西需要学习,并且会喜欢任何建议。感谢任何试图帮助我的人!

3 个答案:

答案 0 :(得分:2)

首先,在我看来,您对预期输出的假设是不正确的。根据你描述的任务,out应该是这样的:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25]
[1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25] // every 2nd number removed
[1, 3, 7, 9, 13, 15, 19, 21, 25] // every 3rd number removed
[1, 3, 7, 13, 15, 19, 25] // every 4th number removed
[1, 3, 7, 13, 19, 25] // every 5th number removed
[1, 3, 7, 13, 19] // 6th number removed = final output

除此之外,我看到两个错误。

  1. 您想要删除“每第n个数字”,因此您不希望在列表中测试值,而是测试它们的位置/索引。
  2. 每当从ArrayList中删除元素时,以下元素的索引和列表的大小减1.意味着如果从删除“2”开始,则要删除的下一个数字将是“5” “,而不是所需的”4“(假设您正在测试索引,而不是值)。一种解决方案是从列表末尾开始测试索引。在这种情况下,删除元素后更高的索引会发生变化并不重要,因为您已经传递了它们。
  3. 修改

    针对@Kedar Mhaswade的请求编写的答案提供了一些代码,以便测试如何从列表末尾删除元素。

    这是我的第一个方法:

      List<Integer> removeBackward(List<Integer> numbers) {
        int count, sPos;
        count = 2;
        while(count<=numbers.size())
        {
          for(sPos = numbers.size(); sPos >= numbers.size()-count; sPos--)
          {
            if(0 == sPos%count) {
              break;
            }
          }
          for(int pos = sPos; pos > 0; pos=pos-count)
          {
            numbers.remove(pos-1);
          }
          count++;           
        } 
        return numbers;
      }
    

    我做了一些测试(见下文),结果是它对小组数字(&lt; 12000)表现得非常好。在较大的集合上,@ Kedar Mhaswade的第二种方法(保留要保留的元素的额外列表)优于这种方法。

    因此我尝试了第二种方法:

    这个想法是,当第一步中删除一半元素并且要保留的元素数量逐步减少时,不需要为保留元素维护第二个列表。 因此,我只是将元素移动到相同列表的末尾并维护其他指针,以便了解保留元素的范围。在流程结束时,只需从列表末尾提取最终结果。

    List<Integer> retainedAtEnd(List<Integer> numbers) {
      int removeX, baseQty, retainedQty, basePos, retainedPos;
      removeX = 1;
      baseQty = numbers.size();
      while(removeX <= baseQty)
      {
        removeX++;
        basePos = numbers.size();
        retainedPos = basePos;
        retainedQty = 0;
        for(int checkPos = baseQty; checkPos >= 1; checkPos--)
        {
          if(0 != checkPos%removeX) 
          {
            basePos = numbers.size()-baseQty+checkPos;
            numbers.set(retainedPos-1, numbers.get(basePos-1));
            retainedPos--;
            retainedQty++;
          }
        }
        baseQty = retainedQty;
      }
      return numbers.subList(numbers.size()-baseQty, numbers.size());
      // return new ArrayList(numbers.subList(numbers.size()-baseQty, numbers.size()));
    }
    

    根据我的测试,不幸的是,这种方法在小集合(&lt; 12000)上表现不佳。它无法与第一种方法或@Kedar Mhaswade的第二种方法竞争,但在更大的方面,它优于两者。

    以下是我测试的方式:

        public void test() {
          int n = 1000;     
          long start;
          System.out.println("Testing with " + n + " numbers ..."); 
    
          System.out.println("Test removing elements backwards:");
          List<Integer> numbers1 = Stream.iterate(1, k -> k + 1).limit(n).collect(Collectors.toList());
          start = System.nanoTime();    
          List<Integer> out1 = this.removeBackward(numbers1);
          System.out.println("Time taken:" + (System.nanoTime() - start));
      //    System.out.println(out1);
    
          System.out.println("Test maintaining extra list for retained elements:");
          List<Integer> numbers2 = Stream.iterate(1, k -> k + 1).limit(n).collect(Collectors.toList());
          start = System.nanoTime();    
          List<Integer> out2 = this.extraRetainedList(numbers2);
          System.out.println("Time taken:" + (System.nanoTime() - start));
      //    System.out.println(out2);
    
          System.out.println("Test collecting retained elements at end of list:");
          List<Integer> numbers3 = Stream.iterate(1, k -> k + 1).limit(n).collect(Collectors.toList());
          start = System.nanoTime();    
          List<Integer> out3 = this.retainedAtEnd(numbers3);
          System.out.println("Time taken:" + (System.nanoTime() - start));
      //    System.out.println(out3);
    
          System.out.println("Test maintaining extra list for elements to remove:");
          List<Integer> numbers4 = Stream.iterate(1, k -> k + 1).limit(n).collect(Collectors.toList());
          start = System.nanoTime();    
          List<Integer> out4 = this.extraDeletedList(numbers4);
          System.out.println("Time taken:" + (System.nanoTime() - start));
      //    System.out.println(out4);
        }
    

答案 1 :(得分:1)

这是一个棘手的问题!我以稍微不同的方式进行了讨论,并使用另一个列表来存储已删除的元素。这是必需的,因为我选择了数据结构。由于我只想使用整数而我使用ArrayList,因此每次删除元素时,列表都会立即调整。我们真正需要做的是标记要删除的元素。有多种方法可以做到这一点,但我选择维护另一个已删除元素列表(因为所有元素都是唯一的,所以使用这个想法很好。)

这是我的第一次尝试:

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/** <p>
 *      Find the lucky numbers amongst natural numbers from 1 to n.
 *      Here's how you find <a href="http://stackoverflow.com/questions/35673769/java-program-to-find-lucky-numbers-from-0-to-n-using-lists">Lucky numbers</a>.
 * </p>
 * Created by kmhaswade on 2/27/16.
 */
public class Lucky {
    public static void main(String[] args) {
        printLucky1(Integer.valueOf(args[0]));
    }
    private static void printLucky1(int n) {
        List<Integer> numbers = Stream.iterate(1, k -> k + 1).limit(n).collect(Collectors.toList());
        System.out.println(numbers);
        int delIndex = 1; // index of the element to be removed, we start with 2nd element
        while (delIndex < numbers.size()) {
            List<Integer> deleted = new ArrayList<>();
            for (int i = delIndex; i < numbers.size(); i += (delIndex + 1)) {
                deleted.add(numbers.get(i));
            }
            numbers.removeAll(deleted); // expensive operation!
            System.out.println(numbers);
            delIndex += 1;
        }
        System.out.println("number of lucky numbers:" + numbers.size());
        System.out.println(numbers);
    }
}

这个有效!但是对于很长的列表,这是非常慢的,因为操作很昂贵:numbers.removeAll(deleted) - 我们正在删除ArrayList中必须移动每个<上的所有受影响元素的一堆元素/ em>删除!

例如,使用前100_000个自然数,我的计算机上大约需要10秒钟。显然,我寻找替代方案。如果我们设计另一个列表并收集我们想要保留的元素,然后在下一次迭代中,这个保留元素列表成为我们要操作的列表怎么办?它看起来会更好,因为没有删除涉及的元素。您仍需要另一个ArrayList 收集元素。在分析术语中,这是O(n)个额外空格(或c.n c ~ 0.5}。

这是我的第二次尝试:

private static void printLucky2(int n) {
    List<Integer> numbers = Stream.iterate(1, k -> k + 1).limit(n).collect(Collectors.toList());
    System.out.println(numbers);
    int delIndex = 1; // index of the element to be removed, we start with 2nd element
    while (delIndex < numbers.size()) {
        List<Integer> retained = new ArrayList<>();
        for (int i = 0; i < numbers.size(); i += 1)
            if ((i+1) % (delIndex + 1) != 0)
                retained.add(numbers.get(i));
        numbers = retained;
        System.out.println(numbers);
        delIndex += 1;
    }
    System.out.println("number of lucky numbers:" + numbers.size());
    System.out.println(numbers);
}

可能会有更多改进,因为对于非常大的输入,此算法所花费的时间可能仍然是不可接受的(将对此改进起作用)。但我已经看到两个数量级的改进!

这是complete code。我确保两个方法都返回相同的列表(list1.equals(list2)返回true),这是我计算机上的输出(前100_000个数字):

[1, 3, 7, ...]
number of lucky numbers: 357
time taken: 6297
number of lucky numbers: 357
[1, 3, ...]
time taken: 57

答案 2 :(得分:0)

对于任何仍然感兴趣的人,我找到了另一种方法。 它不是革命性的或任何东西,它使用了这个线程上的人告诉我的很多东西,但它总结了我猜的所有建议。 我觉得发布我的回答是公平的。 因此,它涉及遍历每个元素,存储在每轮计数中需要删除的所有元素,然后使用removeAll函数在计数增加之前删除它们。 这是所有感兴趣的人的完整代码。任何评论也欢迎。

import java.util.*;
class luckyy
{
    public static void main(String args[])
    {        
    Scanner scan = new Scanner(System.in);
    System.out.println("Enter n: ");       
    int i, n, index;
    n = scan.nextInt();     
    ArrayList <Integer> ele = new ArrayList <Integer> (n);
    ArrayList <Integer> toRemove = new ArrayList <Integer> (n);
    //storing in a list
    for(i = 1;i<=n;i++)
    {
        ele.add(i);
    }
    System.out.println(ele);
    int count = 2;     
    System.out.println("Size: "+ele.size());
    while(count<=ele.size())
    {

        for(i = 0;i<ele.size();i++)
        {
            if((i+1)%count == 0)
            {                    
                toRemove.add(ele.get(i));
            }                
        }
        ele.removeAll(toRemove);
        toRemove.clear();
        count++;
    }
     System.out.println(ele);
}
}

它就是!它有效,男孩我很高兴!如果有人有任何进一步的建议,或其他任何我需要学习或结帐,欢迎您发表评论。 再次感谢大家!