给定n个文件的长度,请在d个线程之间平均分配总字节数,以使任何两个线程相差最多1个字节

时间:2019-07-04 16:38:25

标签: java multithreading algorithm data-structures file-handling

系统会为您提供文件名列表及其长度(以字节为单位)。

示例:

文件1:200文件2:500文件3:800

您将得到一个数字N。我们要启动N个线程来并行读取所有文件,以便每个线程大约读取相等数量的字节 您应该返回N个列表。每个列表描述一个线程的工作:例如,当N = 2时,有两个线程。在上面的示例中,总共有1500个字节(200 + 500 + 800)。划分的合理方法是每个线程读取750个字节。这样您将返回:

两个列表

列表1:文件1:0-199文件2:0-499文件3:0-49 ----------------共750个字节

列表2:文件3:50-799 --------------------总750字节

实施以下方法

List<List<FileRange>> getSplits(List<File> files, int N) 
Class File { 
String filename; long length } 
Class FileRange { 
String filename Long startOffset Long endOffset } 
I tried with this one but it's not working any help would be highly appreciated.
List<List<FileRange>> getSplits(List<File> files, int n) {
        List<List<FileRange>> al=new ArrayList<>();
        long s=files.size();
        long sum=0;
        for(int i=0;i<s;i++){
            long l=files.get(i).length;
            sum+=(long)l;
        }
        long div=(long)sum/n; // no of bytes per thread
        long mod=(long)sum%n;
        long[] lo=new long[(long)n];
        for(long i=0;i<n;i++)
        lo[i]=div;
        if(mod!=0){
            long i=0;
            while(mod>0){
                lo[i]+=1;
                mod--;
                i++;
            }
        }
        long inOffset=0;
        for(long j=0;j<n;j++){
            long val=lo[i];
        for(long i=0;i<(long)files.size();i++){
            String ss=files.get(i).filename;
            long ll=files.get(i).length;
                if(ll<val){
                    inOffset=0;
                    val-=ll;
                }
                else{
                    inOffset=ll-val;
                    ll=val;
                }
                al.add(new ArrayList<>(new File(ss,inOffset,ll-1)));
            }
        }

    }

我在startOffset和endOffset中遇到了相应的文件问题。我试过了,但是我无法从List中提取出来并以必需的返回类型List>的形式添加。

2 个答案:

答案 0 :(得分:1)

以下是您问题的代码解决方案(使用Java):

自定义类'File'和'FileRange'如下:

public class File{

    String filename;
    long length;

    public File(String filename, long length) {
        this.filename = filename;
        this.length = length;
    }

    public String getFilename() {
        return filename;
    }

    public void setFilename(String filename) {
        this.filename = filename;
    }

    public long getLength() {
        return length;
    }

    public void setLength(long length) {
        this.length = length;
    }
}
public class FileRange {

    String filename;
    Long startOffset;
    Long endOffset;

    public FileRange(String filename, Long startOffset, Long endOffset) {
        this.filename = filename;
        this.startOffset = startOffset;
        this.endOffset = endOffset;
    }

    public String getFilename() {
        return filename;
    }

    public void setFilename(String filename) {
        this.filename = filename;
    }

    public Long getStartOffset() {
        return startOffset;
    }

    public void setStartOffset(Long startOffset) {
        this.startOffset = startOffset;
    }

    public Long getEndOffset() {
        return endOffset;
    }

    public void setEndOffset(Long endOffset) {
        this.endOffset = endOffset;
    }
}

主要类别如下:


import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.concurrent.atomic.AtomicInteger;

public class MainClass {

    private static List<List<FileRange>> getSplits(List<File> files, int N) {

        List<List<FileRange>> results = new ArrayList<>();

        long sum = files.stream().mapToLong(File::getLength).sum();     // Total bytes in all the files

        long div = sum/N;

        long mod = sum%N;

        // Storing how many bytes each thread gets to process
        long thread_bytes[] = new long[N];

        // At least 'div' number of bytes will be processed by each thread
        for(int i=0;i<N;i++)
            thread_bytes[i] = div;

        // Left over bytes to be processed by each thread
        for(int i=0;i<mod;i++)
            thread_bytes[i] += 1;

        int count = 0;

        int len = files.size();

        long processed_bytes[] = new long[len];

        long temp = 0L;

        int file_to_be_processed = 0;

        while(count < N && sum > 0) {

            temp = thread_bytes[count];

            sum -= temp;

            List<FileRange> internal = new ArrayList<>();

            while (temp > 0) {

                // Start from the file to be processed - Will be 0 in the first iteration
                // Will be updated in the subsequent iterations
                for(int j=file_to_be_processed;j<len && temp>0;j++){

                    File f = files.get(j);

                    if(f.getLength() - processed_bytes[j] <= temp){

                        internal.add(new FileRange(f.getFilename(), processed_bytes[j], f.getLength()- 1));

                        processed_bytes[j] = f.getLength() - processed_bytes[j];

                        temp -= processed_bytes[j];

                        file_to_be_processed++;

                    }

                    else{

                        internal.add(new FileRange(f.getFilename(), processed_bytes[j], processed_bytes[j] + temp - 1));

                        // In this case, we won't update the number for file to be processed
                        processed_bytes[j] += temp;

                        temp -= processed_bytes[j];

                    }

                }

                results.add(internal);

                count++;

            }

        }
        return results;
    }

    public static void main(String args[]){

        Scanner scn = new Scanner(System.in);
        int N = scn.nextInt();

        // Inserting demo records in list

        File f1 = new File("File 1",200);
        File f2 = new File("File 2",500);
        File f3 = new File("File 3",800);

        List<File> files = new ArrayList<>();

        files.add(f1);
        files.add(f2);
        files.add(f3);

        List<List<FileRange>> results = getSplits(files, N);

        final AtomicInteger result_count = new AtomicInteger();

        // Displaying the results

        results.forEach(result -> {

            System.out.println("List "+result_count.incrementAndGet() + " : ");

            result.forEach(res -> {

                System.out.print(res.getFilename() + " : ");
                System.out.print(res.getStartOffset() + " - ");
                System.out.print(res.getEndOffset() + "\n");

            });

            System.out.println("---------------");

        });

    }

}

如果仍然不清楚某个部分,请考虑一种情况,然后空运行该程序。 说 999 个字节必须由 100 个线程处理 因此,100个线程每个获取9个字节,其余99个字节中的每个线程,除第100个线程之外的每个线程获取1个字节。这样,我们将确保2个线程之间最多相差1个字节。继续这个想法,并跟进代码。

答案 1 :(得分:0)

问题的实质是同时浏览两个列表:

  • 输入列表,它是文件列表
  • 输出列表,它是线程列表(每个线程都有一个范围列表)

我发现解决此类问题的最简单方法是无限循环,看起来像这样:

while (1)
{
    move some information from the input to the output
    decide whether to advance to the next input item
    decide whether to advance to the next output item
    if we've reached (the end of the input _OR_ the end of the output)
        break
    if we advanced to the next input item
        prepare the next input item for processing
    if we advanced to the next output item
        prepare the next output item for processing
}

要跟踪输入,我们需要以下信息

  • fileIndex到文件列表的索引
  • fileOffset文件中第一个未分配字节的偏移量,最初为0
  • fileRemain文件中未分配的字节数,最初是文件大小

要跟踪输出,我们需要

  • threadIndex我们当前正在处理的线程的索引(这是算法生成的List<List<FileRange>>中的第一个索引)
  • threadNeeds线程仍需要的字节数,最初为basebase+1

侧面说明:我使用base作为分配给每个线程(sum/n的最小字节数,并使用extra作为获得额外字节({ {1}}。

所以现在我们进入算法的核心:从输入到输出要移动什么信息:

  • 如果sum%n小于fileRemain,则文件的其余部分(可能是整个文件)将分配给当前线程,然后我们移至下一个文件
  • 如果threadNeeds大于fileRemain,则文件的一部分将分配给当前线程,然后我们移至下一个线程
  • 如果threadNeeds等于fileRemain,则将文件的其余部分分配给该线程,然后我们移至下一个文件和下一个线程

通过比较threadNeedsfileRemain并选择两者中的最小值的threadNeeds可以轻松处理这三种情况。

请记住,以下一些伪代码可以帮助您入门:

byteCount

调试技巧:到达输入的结尾和输出的结尾应该同时发生。换句话说,您应该在线程耗尽的同时完全耗尽文件。因此,在开发过程中,我将检查两个条件,并验证它们是否确实在同时更改。