0

I have a problem with my code, it takes too long to run the program when I call the method somme_2, and I want to reduce the run time. By the way the txt file that I'm using in this program contains almost 500_000 lines. Do you have any idea how to fix it ?

This is my main

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Main {
    public static void main(String[] args) throws IOException {
        Somme2 somme2 = new Somme2("src/dico.txt");
        //somme2.remove(alphabeticalOrder("Amsterdam" + "ADN"), "adn");
        //somme2.remove(alphabeticalOrder("Amsterdam" + "ADN"), "riflassent");
        somme2.somme_2(alphabeticalOrder("volontiers" + "tranquillement"));

    }

    private static String alphabeticalOrder(String word) {
        word = word.toLowerCase();
        List<Character> list = new ArrayList<>();
        for (int i = 0; i < word.length(); i++) {
            list.add(word.charAt(i));
        }
        Collections.sort(list);
        String string = "";
        for (int i = 0; i < list.size(); i++) {
            string = string + list.get(i);
        }
        return string;
    }
}

And this is my class which contain the somme_2 function :

import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;

public class Somme2 {
    private String path;

    public Somme2(String path) {
        this.path = path;
    }

    /***
     * values() returns a list which contains every word in our file.
     * @return List<String>
     * @throws FileNotFoundException
     */
    private List<String> values() throws IOException {
        return Files.readAllLines(Path.of(path));
    }

    /***
     * alphabeticalOrder() returns the word in the parameter, alphabetically
     * @param word
     * @return String
     */
    private String alphabeticalOrder(String word) {
        word = word.toLowerCase();
        char[] chars = word.toCharArray();
        Arrays.sort(chars);
        return new String(chars);
    }

    /***
     * addToHashMap() returns a hashMap, which uses as a key alphabetically words, and as value classical words
     * @return HashMap<String, List<String>>
     * @throws FileNotFoundException
     */
    private HashMap<String, List<String>> addToHashMap() throws IOException {
        HashMap<String, List<String>> hashMap = new HashMap<>();
        for (String value : values()) {
            String word = alphabeticalOrder(value);
            if (hashMap.containsKey(word)) {
                hashMap.get(word).add(value);
            }
            else {
                List<String> list = new ArrayList<>();
                list.add(value);
                hashMap.put(word, list);
            }
        }
        return hashMap;
    }

    /***
     * findTheLetter return the index of the letter
     * @param letter
     * @param word
     * @return
     */
    private int findTheLetter(char letter, char[] word) {
        for (int i = 0; i < word.length; i++) {
            if (word[i] == letter) {
                return i;
            }
        }
        return 0;
    }

    private char[] removeLetter(char letter, char[] word) {
        int x = findTheLetter(letter, word);
        char[] newWord;
        if (word.length == 1) {
            newWord = new char[word.length];
        } else {
            newWord = new char[word.length - 1];
        }
        for (int i = 0; i < word.length - 1; i++) {
            if (i < x) {
                newWord[i] = word[i];
            }
            else {
                newWord[i] = word[i + 1];
            }
        }
        return newWord;
    }

    public String remove(String word, String string) {
        char[] myWord = string.toCharArray();
        char[] words = word.toCharArray();
        for (int i = 0; i < string.length(); i++) {
            words = removeLetter(myWord[i], words);
        }
        return new String(words);
    }


    /***
     * somme_2
     * @param alph
     * @throws FileNotFoundException
     */
    public void somme_2(String alph) throws IOException {
        HashMap<String, List<String >> hashMap = addToHashMap();
        List<String> strings = new ArrayList<>();
        strings.addAll(hashMap.keySet());
        String alphWord = "" + alph;
        for (String string : strings) {
            if (hashMap.containsKey(remove(alphabeticalOrder(alphWord), string))) {
                System.out.println("\"" + hashMap.get(string) + "\" and \"" + hashMap.get(remove(alphWord, string)) + "\" give \"" + alphWord + "\"");
                return;
            }
        }
        System.out.println("There are no words");
    }
}

in case you wanna know, this is a part of the txt file :

A
ABS
ADN
ADNc
ADP
ADSL
AIEA
ARN
ARNm
ASBL
ASC
ASCII
AUD
Aarhus
Aaron
Aarschot
Abbeville
Abd
Abdelkader
Abel
Abidjan
Abitibi-Témiscamingue
Abkhazie
Abraham
Abu
Abuja
Abymes
Abyssinie
Acadie
Acapulco
Accra
Achaïe
Achgabat

I just fix the time problem, but now my program sometimes shows result like : "[constitutionnaliserions]" and "[V, v]" give "aeeeiilllmnnnooqrrstttuv"

which is wrong because "constitutionnaliserions" + "V" alphabetically ordered don't give "aeeeiilllmnnnooqrrstttuv".

Thank you for your help !

Alonso
  • 27
  • 5

3 Answers3

0

You can do one thing for sure. Let me list what all you can do to improve your runtime:

  1. Move word.length() outside for loop like: String wordSize=word.length();
  2. Move strings.size() - 1 outside the for loop like int stringsSize=strings.size()-1;
  3. In case of Java you can choose TreeSet which is implementation of Set interface. You can pass Collection object to the constructor of this class which will automatically sort the objects. Also when you add any object then it will be placed in sorted order.
  4. Also don't system.out.println as it is synchronized method, better log your output to a file location..

Hope this answer helps you in improving your runtime.

  • Hello, I did 2 of your advices, I can't do the third one because I must use a HashMap, and the programme runtime is still too long, like at least 2 or 3 min – Alonso Mar 11 '21 at 14:47
0

There are a lot of ways to improve your code.

Lets start with the most important part: algorithm

You iterate over all possible pairs and check the condition, but you can iterate only 1 time over all the words and for each word try to find "complementary" word.

Example: alph = 'aabbcc' and on current iteration over all words we have word = 'acc'. And we want to find another word in our file (complementaryWord), which combined with word will give us alph. Obviously, this complementaryWord should be abb (abb + acc = aabbcc), and its easy to find this word, because you already have hashmap with them.

Overall, it will improve complexity from O(n^2) to O(n).

Code imporvements

  • alphabeticalOrder is doing a lot of unnecessary object allocaions and overall is doesn't look good. Try working here directly with char[]. Also, if you know that words could contain only specific set of letters (like only latin or some other alphabet) you can use bucket sort to improve time complexity.

  • Scanner is slow for big input files. Use, for example, BufferedRead or read lines using newer Files.readAllLines().

  • remove duplicated computations: for example, compute alphabeticalOrder(value) once and store result in variable.

  • in addToHashMap() use computeIfAbsent()to make it shorter and clearer

  • use equals() to compare string instead of hashCode(). As noted in the comments, if s1.hashCode() == s2.hashCode() it doesnt mean that s1.equals(s2).

Flame239
  • 1,274
  • 1
  • 8
  • 20
  • I did everything except the computeIfAbsent(), because I tried and I don't understand how to you use it. But that still too long like " 3 min at least – Alonso Mar 11 '21 at 15:56
  • @alonso00235 please add your new code in the original question – Flame239 Mar 11 '21 at 16:20
  • @alonso00235 You didn't implement the algorithm, which will give you 90% time decrease. Also please post the code without redundant comments and only with the methods that are relevant to the question (i.e. remove all unused methods from your code), it will much easier for us to read it – Flame239 Mar 11 '21 at 18:26
  • I just update my post, if you wanna see =) – Alonso Mar 11 '21 at 23:37
0

Ok I just add an if to check if my test was right and that works thanks for helping !

    /***
     * somme_2
     * @param alph
     * @throws FileNotFoundException
     */
    public void somme_2(String alph) throws IOException {
        HashMap<String, List<String >> hashMap = addToHashMap();
        List<String> strings = new ArrayList<>();
        strings.addAll(hashMap.keySet());
        String alphWord = "" + alph;
        for (String string : strings) {
            if (hashMap.containsKey(remove(alphabeticalOrder(alphWord), string))) {
                if (alphabeticalOrder(string + remove(alphabeticalOrder(alphWord), string)).equals(alphabeticalOrder(alphWord))) {
                    System.out.println("\"" + hashMap.get(string) + "\" and \"" + hashMap.get(remove(alphWord, string)) + "\" give \"" + alphWord + "\"");
                    System.out.println("\"" + string + "\" and \"" + remove(alphWord, string) + "\" give \"" + alphWord + "\"");
                    return;
                }
            }
        }
        System.out.println("There are no words");
    }
Alonso
  • 27
  • 5